Re: Proposal for encrypting pg_shadow passwords - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: Proposal for encrypting pg_shadow passwords
Date
Msg-id 200108150552.f7F5qZV16322@candle.pha.pa.us
Whole thread Raw
Responses Re: Re: Proposal for encrypting pg_shadow passwords  (Bruce Momjian <pgman@candle.pha.pa.us>)
Re: Re: Proposal for encrypting pg_shadow passwords  (Peter Eisentraut <peter_e@gmx.net>)
Re: Re: Proposal for encrypting pg_shadow passwords  (Bruce Momjian <pgman@candle.pha.pa.us>)
Re: Re: Proposal for encrypting pg_shadow passwords  (Peter Eisentraut <peter_e@gmx.net>)
List pgsql-patches
Attached please find:

    the original proposal to encrypt pg_shadow
    a diff of the current CVS
    two new files (backend/libpq/md5.c and include/libpq/md5.h)
        which implement MD5 encryption (from Vince with cleanups)

I have increased the protocol version from 2.0 -> 2.1.  I use MD5 for
all client encryption if the client supports it.  I know we have
portability problems with libc's crypt() this will fix that right away.
Of course older clients and servers will still talk using libc's
crypt().

It turns out that ODBC doesn't even do crypt authentication so I have
not added MD5 code there yet.  That will be another project because I
have to add salt handling and stuff.  Perhaps someone with more ODBC
experience can do it.  If they add crypt(), I can add MD5.  I see code
in connection.c:

                        case AUTH_REQ_CRYPT:
                        case AUTH_REQ_MD5:
                            self->errormsg = "Password crypt authentication not supported";
                            self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
                            return 0;

I added enough so MD5 will fail just like crypt.  Woo-hoo.

JDBC needs to be done, but I am told MD5 is native to Java.

I have made no changes to pg_dump because it will reload the password
just as it was dumped, e.g. if they were encrypted, they will be loaded
encrypted.  If plaintext, they will load plaintext, unless they have
changed postgresql.conf to true, in which case they will be encrypted on
load if they weren't already encrypted.  I didn't see any value to
adding ENCRYPTED/UNENCRYPTED to pg_dump.  Those flags are only useful
for overriding postgresql.conf, but in this case the encryption has
already happened.

I think there is a problem with secondary password files.  Because the
client uses MD5 for encryption, it can't be used to check against
crypt() based secondary passwords.  If those passwords where MD5, it
would be OK.  The problem here is that the client doesn't check to see
if it is going to use the secondary password or the pg_shadow password
until _after_ it gets the password from the client.  I could reorganize
the code but I want to get people's opinion first.  In fact, if we have
portability problems with libc crypt(), should we be using crypt()-based
secondary passwords at all?  I know there was a report just this week
about that.  Perhaps it would be better to remove the entire secondary
password mechanism and allow a list of valid username to appear in
pg_hba.conf in the AUTH_ARGUMENT column.  I think listing usernames that
can connect to the database is the most valuable thing about secondary
password files anyway.  I know you can copy /etc/shadow and use that but
the crypt() problem seems insurmountable and I know others aren't
excited about that feature.

Oh, well, that's about it.  Seems to work pretty well.


> Here is my proposal to fix the security problem of storing cleartext
> passwords in pg_shadow.  The solution is somewhat complex because we
> have to allow 7.2 servers to communicate with 7.1 clients, at least for
> a short while.
>
> Here is a summary of what we currently do and proposed solutions.
>
>
> PG_HBA.CONF
> -----------
> pg_hba.conf has three authentication options of interest to this
> discussion:
>
> trust:  no authentication required
>
> password:  plaintext password is sent over network from client
>        to server
>
> crypt:  random salt is sent to client;  client encrypts using that salt
> and returns encrypted password to server.  Server encrypts pg_shadow
> password with same random salt and compares.  This is why current
> pg_shadow password is cleartext.  (Call this "crypt authentication".)
>
>
> DOUBLE ENCRYPTION
> -----------------
> The solution for encrypting pg_shadow passwords is to encrypt using a
> salt when stored in pg_shadow, and to generate a random salt for each
> authentication request.  Send _both_ salts to the client, let the client
> double encrypt using the pg_shadow salt first, then the random salt, and
> send it back.  The server encrypt using only the random salt and
> compares.
>
> As soon as we encrypt pg_shadow passwords, we can't communicate with
> pre-7.2 clients using crypt-authentication.  Actually, we could, but we
> would have to send the same pg_shadow salt every time, which is insecure
> because someone snooping the wire could just play back the same reply so
> it is better to just fail such authentications.
>
>
> USER INTERFACE
> --------------
> So, my idea is to add an option to CREATE/ALTER USER:
>
>     CREATE USER WITH ENCRYPTED PASSWORD 'fred';
>     CREATE USER WITH UNENCRYPTED PASSWORD 'fred';
>     ALTER USER WITH ENCRYPTED PASSWORD 'fred';
>     ALTER USER WITH UNENCRYPTED PASSWORD 'fred';
>
> Keep in mind ENCRYPTED/UNENCRYPTED controls how it is stored in
> pg_shadow, not wither "fred" is a cleartext or preencrypted password.
> We plan to prefix md5 passwords with "md5" to handle this issue.  (Md5
> passwords are also 35-characters in length.)
>
> Also add a new GUC config option:
>
>     SET password_encrypted_default TO 'OFF';
>
> It would ship as OFF in 7.2 and can be removed in a later release.  Once
> all clients are upgraded to 7.2, you can change the default to ON and do
> ALTER USER WITH PASSWORD 'fred' to encrypt the pg_shadow passwords.  The
> passwords are in cleartext in pg_shadow so it is easy to do.
>
>
> MD5
> ---
> I assume we will use MD5 for encryption of pg_shadow passwords.  The
> letters "md5" will appear at the start of the password string and it
> will be exactly 35 characters.  Vince sent me the code.  We will need to
> add MD5 capability to libpq, ODBC, and JDBC.  (I hope JDBC will not be a
> problem.)  When using CREATE/ALTER user, the system will automatically
> consider a 35-character string that starts with "md5" to be a
> pre-md5-encrypted password, while anything else will be md5 encrypted.
>
>
> SECONDARY PASSWORD FILES
> ------------------------
> To add complexity to this, we also support secondary password files.
> (See pg_hba.conf.sample and pg_password manual in CVS for updated
> descriptions.)  These password files allow encrypted passwords in the
> same format as they appear in traditional /etc/passwd.  (Call this
> crypt-style passwords.)  I realize most BSD's use MD5 in /etc/shadow
> now.
>
> Right now we can use passwords from the file only if we use
> password-authentication.  We can't use crypt-authentication because the
> passwords already have a salt and we don't want to sent the same salt
> every time.  One nice feature of secondary passwords is you can copy
> /etc/passwd or /etc/shadow and use that as your secondary password file
> for PostgreSQL.  I don't know how many people use that but it is nice
> feature.  Remember the secondary password files sit in /data which is
> readable only by the PostgreSQL install user.
>
>
> DOUBLE-CRYPT ENCRYPTION
> -----------------------
> So, we are going to add a new double-MD5 encryption protocol to allow
> pg_shadow passwords to be encrypted.  Do we also add a
> double-crypt-style-password protocol to allow crypt-authentication with
> secondary password files that use crypt-style passwords or just require
> the secondary password files to use MD5?
>
>
> Comments?
>
>
> --
>   Bruce Momjian                        |  http://candle.pha.pa.us
>   pgman@candle.pha.pa.us               |  (610) 853-3000
>   +  If your life is a hard drive,     |  830 Blythe Avenue
>   +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026
Index: doc/src/sgml/client-auth.sgml
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v
retrieving revision 1.15
diff -c -r1.15 client-auth.sgml
*** doc/src/sgml/client-auth.sgml    2001/08/01 23:25:39    1.15
--- doc/src/sgml/client-auth.sgml    2001/08/15 05:12:48
***************
*** 205,215 ****
           <para>
            Like the <literal>password</literal> method, but the password
            is sent over the wire encrypted using a simple
!           challenge-response protocol. This is still not
!           cryptographically secure but it protects against incidental
            wire-sniffing. The name of a file may follow the
!           <literal>crypt</literal> keyword that contains a list of users
!           that this record pertains to.
           </para>
          </listitem>
         </varlistentry>
--- 205,214 ----
           <para>
            Like the <literal>password</literal> method, but the password
            is sent over the wire encrypted using a simple
!           challenge-response protocol. This protects against incidental
            wire-sniffing. The name of a file may follow the
!           <literal>crypt</literal> keyword.  It contains a list of users
!           for this record.
           </para>
          </listitem>
         </varlistentry>
Index: doc/src/sgml/protocol.sgml
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/protocol.sgml,v
retrieving revision 1.18
diff -c -r1.18 protocol.sgml
*** doc/src/sgml/protocol.sgml    2001/06/22 23:27:48    1.18
--- doc/src/sgml/protocol.sgml    2001/08/15 05:12:49
***************
*** 1295,1301 ****
  </Term>
  <ListItem>
  <Para>
!                 The encrypted (using crypt()) password.
  </Para>
  </ListItem>
  </VarListEntry>
--- 1295,1301 ----
  </Term>
  <ListItem>
  <Para>
!                 The encrypted (using MD5 or crypt()) password.
  </Para>
  </ListItem>
  </VarListEntry>
Index: doc/src/sgml/runtime.sgml
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/runtime.sgml,v
retrieving revision 1.74
diff -c -r1.74 runtime.sgml
*** doc/src/sgml/runtime.sgml    2001/08/09 16:20:43    1.74
--- doc/src/sgml/runtime.sgml    2001/08/15 05:12:52
***************
*** 968,973 ****
--- 968,985 ----
     <para>
      <variablelist>
       <varlistentry>
+       <term>AUSTRALIAN_TIMEZONES (<type>bool</type>)</term>
+       <listitem>
+        <para>
+         If set to true, <literal>CST</literal>, <literal>EST</literal>,
+         and <literal>SAT</literal> are interpreted as Australian
+         timezones rather than as North American Central/Eastern
+         Timezones and Saturday. The default is false.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
        <indexterm>
         <primary>deadlock</primary>
         <secondary>timeout</secondary>
***************
*** 1256,1273 ****
          keyword to exclude subtables. See the SQL language reference
          and the <citetitle>User's Guide</citetitle> for more
          information about inheritance.
-        </para>
-       </listitem>
-      </varlistentry>
-
-      <varlistentry>
-       <term>AUSTRALIAN_TIMEZONES (<type>bool</type>)</term>
-       <listitem>
-        <para>
-         If set to true, <literal>CST</literal>, <literal>EST</literal>,
-         and <literal>SAT</literal> are interpreted as Australian
-         timezones rather than as North American Central/Eastern
-         Timezones and Saturday. The default is false.
         </para>
        </listitem>
       </varlistentry>
--- 1268,1273 ----
Index: doc/src/sgml/ref/alter_user.sgml
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/ref/alter_user.sgml,v
retrieving revision 1.14
diff -c -r1.14 alter_user.sgml
*** doc/src/sgml/ref/alter_user.sgml    2001/07/10 22:09:27    1.14
--- doc/src/sgml/ref/alter_user.sgml    2001/08/15 05:12:52
***************
*** 27,33 ****

  where <replaceable class="PARAMETER">option</replaceable> can be:

!       PASSWORD '<replaceable class="PARAMETER">password</replaceable>'
          | CREATEDB | NOCREATEDB
          | CREATEUSER | NOCREATEUSER
          | VALID UNTIL '<replaceable class="PARAMETER">abstime</replaceable>'
--- 27,33 ----

  where <replaceable class="PARAMETER">option</replaceable> can be:

!       [ ENCRYPTED | UNENCRYPTED ] PASSWORD '<replaceable class="PARAMETER">password</replaceable>'
          | CREATEDB | NOCREATEDB
          | CREATEUSER | NOCREATEUSER
          | VALID UNTIL '<replaceable class="PARAMETER">abstime</replaceable>'
***************
*** 53,62 ****
       </varlistentry>

       <varlistentry>
!       <term><replaceable class="PARAMETER">password</replaceable></term>
        <listitem>
         <para>
      The new password to be used for this account.
         </para>
        </listitem>
       </varlistentry>
--- 53,65 ----
       </varlistentry>

       <varlistentry>
!       <term><replaceable class="PARAMETER">[ encrypted | unencrypted ] password</replaceable></term>
        <listitem>
         <para>
      The new password to be used for this account.
+     <literal>Encrypted</literal>/ <literal>unencrypted</literal>
+     controls whether the password is stored encrypted in the
+     database.
         </para>
        </listitem>
       </varlistentry>
Index: doc/src/sgml/ref/create_user.sgml
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/ref/create_user.sgml,v
retrieving revision 1.17
diff -c -r1.17 create_user.sgml
*** doc/src/sgml/ref/create_user.sgml    2001/07/10 22:09:27    1.17
--- doc/src/sgml/ref/create_user.sgml    2001/08/15 05:12:52
***************
*** 28,34 ****
  where <replaceable class="PARAMETER">option</replaceable> can be:

        SYSID <replaceable class="PARAMETER">uid</replaceable>
!         | PASSWORD '<replaceable class="PARAMETER">password</replaceable>'
          | CREATEDB | NOCREATEDB
          | CREATEUSER | NOCREATEUSER
          | IN GROUP <replaceable class="PARAMETER">groupname</replaceable> [, ...]
--- 28,34 ----
  where <replaceable class="PARAMETER">option</replaceable> can be:

        SYSID <replaceable class="PARAMETER">uid</replaceable>
!         | [ ENCRYPTED | UNENCRYPTED ] PASSWORD '<replaceable class="PARAMETER">password</replaceable>'
          | CREATEDB | NOCREATEDB
          | CREATEUSER | NOCREATEUSER
          | IN GROUP <replaceable class="PARAMETER">groupname</replaceable> [, ...]
***************
*** 72,83 ****
       </varlistentry>

       <varlistentry>
!       <term><replaceable class="parameter">password</replaceable></term>
        <listitem>
         <para>
          Sets the user's password. If you do not plan to use password
          authentication you can omit this option, otherwise the user
          won't be able to connect to a password-authenticated server.
          See the chapter on client authentication in the
      <citetitle>Administrator's Guide</citetitle> for details on
          how to set up authentication mechanisms.
--- 72,90 ----
       </varlistentry>

       <varlistentry>
!       <term><replaceable class="parameter">[ encrypted | unencrypted ] password</replaceable></term>
        <listitem>
         <para>
          Sets the user's password. If you do not plan to use password
          authentication you can omit this option, otherwise the user
          won't be able to connect to a password-authenticated server.
+     </para>
+     <para>
+     <literal>ENCRYPTED/UNENCRYPTED</literal> controls whether the
+     password is stored encrypted in the database. Older clients may
+     have trouble communicating using encrypted password storage.
+     </para>
+     <para>
          See the chapter on client authentication in the
      <citetitle>Administrator's Guide</citetitle> for details on
          how to set up authentication mechanisms.
Index: src/backend/commands/user.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/commands/user.c,v
retrieving revision 1.79
diff -c -r1.79 user.c
*** src/backend/commands/user.c    2001/07/12 18:02:59    1.79
--- src/backend/commands/user.c    2001/08/15 05:12:53
***************
*** 25,30 ****
--- 25,31 ----
  #include "catalog/indexing.h"
  #include "commands/user.h"
  #include "libpq/crypt.h"
+ #include "libpq/md5.h"
  #include "miscadmin.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
***************
*** 34,41 ****


  static void CheckPgUserAclNotNull(void);

-
  /*---------------------------------------------------------------------
   * write_password_file / update_pg_pwd
   *
--- 35,42 ----


  static void CheckPgUserAclNotNull(void);
+ extern bool Password_encryption;

  /*---------------------------------------------------------------------
   * write_password_file / update_pg_pwd
   *
***************
*** 201,272 ****
      int            max_id = -1;
      List       *item, *option;
      char       *password = NULL;    /* PostgreSQL user password */
      int            sysid = 0;            /* PgSQL system id (valid if havesysid) */
      bool        createdb = false;    /* Can the user create databases? */
      bool        createuser = false;    /* Can this user create users? */
      List       *groupElts = NIL;    /* The groups the user is a member of */
      char       *validUntil = NULL;    /* The time the login is valid until */
!     DefElem    *dpassword = NULL;
!     DefElem    *dsysid = NULL;
!     DefElem    *dcreatedb = NULL;
!     DefElem    *dcreateuser = NULL;
!     DefElem    *dgroupElts = NULL;
!     DefElem    *dvalidUntil = NULL;

      /* Extract options from the statement node tree */
      foreach(option, stmt->options)
      {
          DefElem *defel = (DefElem *) lfirst(option);

!         if (strcasecmp(defel->defname, "password") == 0) {
!             if (dpassword)
!                 elog(ERROR, "CREATE USER: conflicting options");
!             dpassword = defel;
!         }
!         else if (strcasecmp(defel->defname, "sysid") == 0) {
!             if (dsysid)
!                 elog(ERROR, "CREATE USER: conflicting options");
!             dsysid = defel;
!         }
!         else if (strcasecmp(defel->defname, "createdb") == 0) {
!             if (dcreatedb)
!                 elog(ERROR, "CREATE USER: conflicting options");
!             dcreatedb = defel;
!         }
!         else if (strcasecmp(defel->defname, "createuser") == 0) {
!             if (dcreateuser)
!                 elog(ERROR, "CREATE USER: conflicting options");
!             dcreateuser = defel;
!         }
!         else if (strcasecmp(defel->defname, "groupElts") == 0) {
!             if (dgroupElts)
!                 elog(ERROR, "CREATE USER: conflicting options");
!             dgroupElts = defel;
!         }
!         else if (strcasecmp(defel->defname, "validUntil") == 0) {
!             if (dvalidUntil)
!                 elog(ERROR, "CREATE USER: conflicting options");
!             dvalidUntil = defel;
!         }
!         else
!             elog(ERROR,"CREATE USER: option \"%s\" not recognized",
!                  defel->defname);
!     }

!     if (dcreatedb)
          createdb = intVal(dcreatedb->arg) != 0;
!     if (dcreateuser)
          createuser = intVal(dcreateuser->arg) != 0;
!     if (dsysid)
      {
          sysid = intVal(dsysid->arg);
          havesysid = true;
      }
!     if (dvalidUntil)
          validUntil = strVal(dvalidUntil->arg);
!     if (dpassword)
          password = strVal(dpassword->arg);
!     if (dgroupElts)
          groupElts = (List *) dgroupElts->arg;

      /* Check some permissions first */
--- 202,281 ----
      int            max_id = -1;
      List       *item, *option;
      char       *password = NULL;    /* PostgreSQL user password */
+     bool        encrypt_password = Password_encryption;    /* encrypt password? */
+     char        encrypted_password[MD5_PASSWD_LEN+1];
      int            sysid = 0;            /* PgSQL system id (valid if havesysid) */
      bool        createdb = false;    /* Can the user create databases? */
      bool        createuser = false;    /* Can this user create users? */
      List       *groupElts = NIL;    /* The groups the user is a member of */
      char       *validUntil = NULL;    /* The time the login is valid until */
!     DefElem    *dpassword = NULL;
!     DefElem    *dsysid = NULL;
!     DefElem    *dcreatedb = NULL;
!     DefElem    *dcreateuser = NULL;
!     DefElem    *dgroupElts = NULL;
!     DefElem    *dvalidUntil = NULL;

      /* Extract options from the statement node tree */
      foreach(option, stmt->options)
      {
          DefElem *defel = (DefElem *) lfirst(option);

!         if (strcasecmp(defel->defname, "password") == 0 ||
!             strcasecmp(defel->defname, "encryptedPassword") == 0 ||
!             strcasecmp(defel->defname, "unencryptedPassword") == 0) {
!             if (dpassword)
!                 elog(ERROR, "CREATE USER: conflicting options");
!             dpassword = defel;
!             if (strcasecmp(defel->defname, "encryptedPassword") == 0)
!                 encrypt_password = true;
!             else if (strcasecmp(defel->defname, "unencryptedPassword") == 0)
!                 encrypt_password = false;
!         }
!         else if (strcasecmp(defel->defname, "sysid") == 0) {
!             if (dsysid)
!                 elog(ERROR, "CREATE USER: conflicting options");
!             dsysid = defel;
!         }
!         else if (strcasecmp(defel->defname, "createdb") == 0) {
!             if (dcreatedb)
!                 elog(ERROR, "CREATE USER: conflicting options");
!             dcreatedb = defel;
!         }
!         else if (strcasecmp(defel->defname, "createuser") == 0) {
!             if (dcreateuser)
!                 elog(ERROR, "CREATE USER: conflicting options");
!             dcreateuser = defel;
!         }
!         else if (strcasecmp(defel->defname, "groupElts") == 0) {
!             if (dgroupElts)
!                 elog(ERROR, "CREATE USER: conflicting options");
!             dgroupElts = defel;
!         }
!         else if (strcasecmp(defel->defname, "validUntil") == 0) {
!             if (dvalidUntil)
!                 elog(ERROR, "CREATE USER: conflicting options");
!             dvalidUntil = defel;
!         }
!         else
!             elog(ERROR,"CREATE USER: option \"%s\" not recognized",
!                  defel->defname);
!     }

!     if (dcreatedb)
          createdb = intVal(dcreatedb->arg) != 0;
!     if (dcreateuser)
          createuser = intVal(dcreateuser->arg) != 0;
!     if (dsysid)
      {
          sysid = intVal(dsysid->arg);
          havesysid = true;
      }
!     if (dvalidUntil)
          validUntil = strVal(dvalidUntil->arg);
!     if (dpassword)
          password = strVal(dpassword->arg);
!     if (dgroupElts)
          groupElts = (List *) dgroupElts->arg;

      /* Check some permissions first */
***************
*** 337,344 ****
      new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser);

      if (password)
!         new_record[Anum_pg_shadow_passwd - 1] =
!             DirectFunctionCall1(textin, CStringGetDatum(password));
      if (validUntil)
          new_record[Anum_pg_shadow_valuntil - 1] =
              DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil));
--- 346,363 ----
      new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser);

      if (password)
!     {
!         if (!encrypt_password || isMD5(password))
!             new_record[Anum_pg_shadow_passwd - 1] =
!                 DirectFunctionCall1(textin, CStringGetDatum(password));
!         else
!         {
!             if (!EncryptMD5(password, stmt->user, encrypted_password))
!                 elog(ERROR, "CREATE USER: password encryption failed");
!             new_record[Anum_pg_shadow_passwd - 1] =
!                 DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
!         }
!     }
      if (validUntil)
          new_record[Anum_pg_shadow_valuntil - 1] =
              DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil));
***************
*** 418,423 ****
--- 437,444 ----
      bool        null;
      List       *option;
      char       *password = NULL;    /* PostgreSQL user password */
+     bool        encrypt_password = Password_encryption;    /* encrypt password? */
+     char        encrypted_password[MD5_PASSWD_LEN+1];
      int            createdb = -1;        /* Can the user create databases? */
      int            createuser = -1;    /* Can this user create users? */
      char       *validUntil = NULL;    /* The time the login is valid until */
***************
*** 431,440 ****
      {
          DefElem *defel = (DefElem *) lfirst(option);

!         if (strcasecmp(defel->defname, "password") == 0) {
              if (dpassword)
                  elog(ERROR, "ALTER USER: conflicting options");
              dpassword = defel;
          }
          else if (strcasecmp(defel->defname, "createdb") == 0) {
              if (dcreatedb)
--- 452,467 ----
      {
          DefElem *defel = (DefElem *) lfirst(option);

!         if (strcasecmp(defel->defname, "password") == 0 ||
!             strcasecmp(defel->defname, "encryptedPassword") == 0 ||
!             strcasecmp(defel->defname, "unencryptedPassword") == 0) {
              if (dpassword)
                  elog(ERROR, "ALTER USER: conflicting options");
              dpassword = defel;
+             if (strcasecmp(defel->defname, "encryptedPassword") == 0)
+                 encrypt_password = true;
+             else if (strcasecmp(defel->defname, "unencryptedPassword") == 0)
+                 encrypt_password = false;
          }
          else if (strcasecmp(defel->defname, "createdb") == 0) {
              if (dcreatedb)
***************
*** 445,461 ****
              if (dcreateuser)
                  elog(ERROR, "ALTER USER: conflicting options");
              dcreateuser = defel;
!         }
          else if (strcasecmp(defel->defname, "validUntil") == 0) {
              if (dvalidUntil)
                  elog(ERROR, "ALTER USER: conflicting options");
              dvalidUntil = defel;
          }
!         else
              elog(ERROR,"ALTER USER: option \"%s\" not recognized",
                   defel->defname);
      }
!
      if (dcreatedb)
          createdb = intVal(dcreatedb->arg);
      if (dcreateuser)
--- 472,488 ----
              if (dcreateuser)
                  elog(ERROR, "ALTER USER: conflicting options");
              dcreateuser = defel;
!         }
          else if (strcasecmp(defel->defname, "validUntil") == 0) {
              if (dvalidUntil)
                  elog(ERROR, "ALTER USER: conflicting options");
              dvalidUntil = defel;
          }
!         else
              elog(ERROR,"ALTER USER: option \"%s\" not recognized",
                   defel->defname);
      }
!
      if (dcreatedb)
          createdb = intVal(dcreatedb->arg);
      if (dcreateuser)
***************
*** 464,471 ****
          validUntil = strVal(dvalidUntil->arg);
      if (dpassword)
          password = strVal(dpassword->arg);
!
!      if (password)
          CheckPgUserAclNotNull();

      /* must be superuser or just want to change your own password */
--- 491,498 ----
          validUntil = strVal(dvalidUntil->arg);
      if (dpassword)
          password = strVal(dpassword->arg);
!
!     if (password)
          CheckPgUserAclNotNull();

      /* must be superuser or just want to change your own password */
***************
*** 552,559 ****
      /* password */
      if (password)
      {
!         new_record[Anum_pg_shadow_passwd - 1] =
!             DirectFunctionCall1(textin, CStringGetDatum(password));
          new_record_nulls[Anum_pg_shadow_passwd - 1] = ' ';
      }
      else
--- 579,594 ----
      /* password */
      if (password)
      {
!         if (!encrypt_password || isMD5(password))
!             new_record[Anum_pg_shadow_passwd - 1] =
!                 DirectFunctionCall1(textin, CStringGetDatum(password));
!         else
!         {
!             if (!EncryptMD5(password, stmt->user, encrypted_password))
!                 elog(ERROR, "CREATE USER: password encryption failed");
!             new_record[Anum_pg_shadow_passwd - 1] =
!                 DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
!         }
          new_record_nulls[Anum_pg_shadow_passwd - 1] = ' ';
      }
      else
***************
*** 793,832 ****
      Datum        new_record[Natts_pg_group];
      char        new_record_nulls[Natts_pg_group];
      List       *item,
!                *option,
                 *newlist = NIL;
      ArrayType  *userarray;
!     int            sysid = 0;
!     List       *userElts = NIL;
!     DefElem    *dsysid = NULL;
!     DefElem    *duserElts = NULL;

      foreach(option, stmt->options)
      {
          DefElem *defel = (DefElem *) lfirst(option);

!         if (strcasecmp(defel->defname, "sysid") == 0) {
!             if (dsysid)
!                 elog(ERROR, "CREATE GROUP: conflicting options");
!             dsysid = defel;
!         }
!         else if (strcasecmp(defel->defname, "userElts") == 0) {
!             if (duserElts)
!                 elog(ERROR, "CREATE GROUP: conflicting options");
!             duserElts = defel;
!         }
!         else
!             elog(ERROR,"CREATE GROUP: option \"%s\" not recognized",
!                  defel->defname);
!     }

!     if (dsysid)
      {
          sysid = intVal(dsysid->arg);
          havesysid = true;
      }

!     if (duserElts)
          userElts = (List *) duserElts->arg;

      /*
--- 828,867 ----
      Datum        new_record[Natts_pg_group];
      char        new_record_nulls[Natts_pg_group];
      List       *item,
!                *option,
                 *newlist = NIL;
      ArrayType  *userarray;
!     int            sysid = 0;
!     List       *userElts = NIL;
!     DefElem    *dsysid = NULL;
!     DefElem    *duserElts = NULL;

      foreach(option, stmt->options)
      {
          DefElem *defel = (DefElem *) lfirst(option);

!         if (strcasecmp(defel->defname, "sysid") == 0) {
!             if (dsysid)
!                 elog(ERROR, "CREATE GROUP: conflicting options");
!             dsysid = defel;
!         }
!         else if (strcasecmp(defel->defname, "userElts") == 0) {
!             if (duserElts)
!                 elog(ERROR, "CREATE GROUP: conflicting options");
!             duserElts = defel;
!         }
!         else
!             elog(ERROR,"CREATE GROUP: option \"%s\" not recognized",
!                  defel->defname);
!     }

!     if (dsysid)
      {
          sysid = intVal(dsysid->arg);
          havesysid = true;
      }

!     if (duserElts)
          userElts = (List *) duserElts->arg;

      /*
Index: src/backend/libpq/Makefile
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/Makefile,v
retrieving revision 1.24
diff -c -r1.24 Makefile
*** src/backend/libpq/Makefile    2000/08/25 10:00:30    1.24
--- src/backend/libpq/Makefile    2001/08/15 05:12:53
***************
*** 15,21 ****
  # be-fsstubs is here for historical reasons, probably belongs elsewhere

  OBJS = be-fsstubs.o \
!     auth.o crypt.o hba.o password.o \
      pqcomm.o pqformat.o pqpacket.o pqsignal.o util.o


--- 15,21 ----
  # be-fsstubs is here for historical reasons, probably belongs elsewhere

  OBJS = be-fsstubs.o \
!     auth.o crypt.o hba.o md5.o password.o \
      pqcomm.o pqformat.o pqpacket.o pqsignal.o util.o


Index: src/backend/libpq/auth.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/auth.c,v
retrieving revision 1.56
diff -c -r1.56 auth.c
*** src/backend/libpq/auth.c    2001/08/07 10:44:13    1.56
--- src/backend/libpq/auth.c    2001/08/15 05:12:53
***************
*** 420,428 ****
              authmethod = "IDENT";
              break;
          case uaPassword:
-             authmethod = "Password";
-             break;
          case uaCrypt:
              authmethod = "Password";
              break;
      }
--- 420,427 ----
              authmethod = "IDENT";
              break;
          case uaPassword:
          case uaCrypt:
+         case uaMD5:
              authmethod = "Password";
              break;
      }
***************
*** 507,512 ****
--- 506,516 ----
              status = recv_and_check_password_packet(port);
              break;

+         case uaMD5:
+             sendAuthRequest(port, AUTH_REQ_MD5);
+             status = recv_and_check_password_packet(port);
+             break;
+
          case uaTrust:
              status = STATUS_OK;
              break;
***************
*** 532,538 ****
      pq_sendint(&buf, (int32) areq, sizeof(int32));

      /* Add the salt for encrypted passwords. */
!     if (areq == AUTH_REQ_CRYPT)
      {
          pq_sendint(&buf, port->salt[0], 1);
          pq_sendint(&buf, port->salt[1], 1);
--- 536,542 ----
      pq_sendint(&buf, (int32) areq, sizeof(int32));

      /* Add the salt for encrypted passwords. */
!     if (areq == AUTH_REQ_CRYPT || areq == AUTH_REQ_MD5)
      {
          pq_sendint(&buf, port->salt[0], 1);
          pq_sendint(&buf, port->salt[1], 1);
***************
*** 557,563 ****
      if (pq_eof() == EOF || pq_getint(&len, 4) == EOF)
          return STATUS_ERROR;    /* client didn't want to send password */
      initStringInfo(&buf);
!     pq_getstr(&buf);

      if (DebugLvl)
          fprintf(stderr, "received password packet with len=%d, pw=%s\n",
--- 561,567 ----
      if (pq_eof() == EOF || pq_getint(&len, 4) == EOF)
          return STATUS_ERROR;    /* client didn't want to send password */
      initStringInfo(&buf);
!     pq_getstr(&buf);        /* receive password */

      if (DebugLvl)
          fprintf(stderr, "received password packet with len=%d, pw=%s\n",
***************
*** 579,585 ****
      if (port->auth_arg[0] != '\0')
          return verify_password(port, user, password);

!     return crypt_verify(port, user, password);
  }


--- 583,589 ----
      if (port->auth_arg[0] != '\0')
          return verify_password(port, user, password);

!     return md5_crypt_verify(port, user, password);
  }


***************
*** 594,600 ****
      MsgType        msgtype = (MsgType) port->proto;

      /* Handle the authentication that's offered. */
-
      switch (msgtype)
      {
          case STARTUP_KRB4_MSG:
--- 598,603 ----
***************
*** 634,639 ****
--- 637,643 ----
      switch (port->auth_method)
      {
          case uaCrypt:
+         case uaMD5:
          case uaReject:
              status = STATUS_ERROR;
              break;
Index: src/backend/libpq/crypt.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/crypt.c,v
retrieving revision 1.32
diff -c -r1.32 crypt.c
*** src/backend/libpq/crypt.c    2001/06/23 23:26:17    1.32
--- src/backend/libpq/crypt.c    2001/08/15 05:12:54
***************
*** 19,24 ****
--- 19,25 ----

  #include "postgres.h"
  #include "libpq/crypt.h"
+ #include "libpq/md5.h"
  #include "miscadmin.h"
  #include "storage/fd.h"
  #include "utils/nabstime.h"
***************
*** 254,260 ****
  /*-------------------------------------------------------------------------*/

  int
! crypt_verify(const Port *port, const char *user, const char *pgpass)
  {

      char       *passwd,
--- 255,261 ----
  /*-------------------------------------------------------------------------*/

  int
! md5_crypt_verify(const Port *port, const char *user, const char *pgpass)
  {

      char       *passwd,
***************
*** 280,288 ****
       * Compare with the encrypted or plain password depending on the
       * authentication method being used for this connection.
       */
!
!     crypt_pwd =
!         (port->auth_method == uaCrypt ? crypt(passwd, port->salt) : passwd);

      if (!strcmp(pgpass, crypt_pwd))
      {
--- 281,327 ----
       * Compare with the encrypted or plain password depending on the
       * authentication method being used for this connection.
       */
!      switch (port->auth_method)
!      {
!         case uaCrypt:
!             crypt_pwd = crypt(passwd, port->salt);
!             break;
!         case uaMD5:
!             crypt_pwd = palloc(MD5_PASSWD_LEN+1);
!
!             if (isMD5(passwd))
!             {
!                 if (!EncryptMD5(passwd + strlen("md5"),
!                                 (char *)port->salt, crypt_pwd))
!                 {
!                     pfree(crypt_pwd);
!                     return STATUS_ERROR;
!                 }
!             }
!             else
!             {
!                 char *crypt_pwd2 = palloc(MD5_PASSWD_LEN+1);
!
!                 if (!EncryptMD5(passwd, port->user, crypt_pwd2))
!                 {
!                     pfree(crypt_pwd);
!                     pfree(crypt_pwd2);
!                     return STATUS_ERROR;
!                 }
!                 if (!EncryptMD5(crypt_pwd2 + strlen("md5"), port->salt,
!                                 crypt_pwd))
!                 {
!                     pfree(crypt_pwd);
!                     pfree(crypt_pwd2);
!                     return STATUS_ERROR;
!                 }
!                 pfree(crypt_pwd2);
!             }
!             break;
!         default:
!             crypt_pwd = passwd;
!             break;
!     }

      if (!strcmp(pgpass, crypt_pwd))
      {
***************
*** 302,310 ****
              retval = STATUS_OK;
      }

!     pfree((void *) passwd);
      if (valuntil)
!         pfree((void *) valuntil);

      return retval;
  }
--- 341,351 ----
              retval = STATUS_OK;
      }

!     pfree(passwd);
      if (valuntil)
!         pfree(valuntil);
!     if (port->auth_method == uaMD5)
!         pfree(crypt_pwd);

      return retval;
  }
Index: src/backend/libpq/hba.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/hba.c,v
retrieving revision 1.61
diff -c -r1.61 hba.c
*** src/backend/libpq/hba.c    2001/08/02 14:39:35    1.61
--- src/backend/libpq/hba.c    2001/08/15 05:12:54
***************
*** 203,210 ****
   *  *error_p.  line points to the next token of the line.
   */
  static void
! parse_hba_auth(List *line, UserAuth *userauth_p, char *auth_arg,
!                 bool *error_p)
  {
      char        *token;

--- 203,210 ----
   *  *error_p.  line points to the next token of the line.
   */
  static void
! parse_hba_auth(List *line, ProtocolVersion proto, UserAuth *userauth_p,
!                 char *auth_arg, bool *error_p)
  {
      char        *token;

***************
*** 227,233 ****
          else if (strcmp(token, "reject") == 0)
              *userauth_p = uaReject;
          else if (strcmp(token, "crypt") == 0)
!             *userauth_p = uaCrypt;
          else
              *error_p = true;
          line = lnext(line);
--- 227,241 ----
          else if (strcmp(token, "reject") == 0)
              *userauth_p = uaReject;
          else if (strcmp(token, "crypt") == 0)
!         {
!             /* if the client supports it, use MD5 */
!             if (PG_PROTOCOL_MAJOR(proto) > 2 ||
!                 (PG_PROTOCOL_MAJOR(proto) == 2 &&
!                  PG_PROTOCOL_MINOR(proto) >= 1))
!                 *userauth_p = uaMD5;
!             else
!                 *userauth_p = uaCrypt;
!         }
          else
              *error_p = true;
          line = lnext(line);
***************
*** 285,291 ****
          line = lnext(line);
          if (!line)
              goto hba_syntax;
!         parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
          if (*error_p)
              goto hba_syntax;

--- 293,300 ----
          line = lnext(line);
          if (!line)
              goto hba_syntax;
!         parse_hba_auth(line, port->proto, &port->auth_method,
!                        port->auth_arg, error_p);
          if (*error_p)
              goto hba_syntax;

***************
*** 354,360 ****
          line = lnext(line);
          if (!line)
              goto hba_syntax;
!         parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
          if (*error_p)
              goto hba_syntax;

--- 363,370 ----
          line = lnext(line);
          if (!line)
              goto hba_syntax;
!         parse_hba_auth(line, port->proto, &port->auth_method,
!                        port->auth_arg, error_p);
          if (*error_p)
              goto hba_syntax;

Index: src/backend/libpq/password.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/password.c,v
retrieving revision 1.37
diff -c -r1.37 password.c
*** src/backend/libpq/password.c    2001/06/18 16:11:30    1.37
--- src/backend/libpq/password.c    2001/08/15 05:12:54
***************
*** 82,92 ****
               * the current code needs non-encrypted passwords to
               * encrypt with a random salt.
               */
!             if (port->auth_method == uaCrypt
!                 || test_pw == NULL || test_pw[0] == '\0'
!                 || strcmp(test_pw, "+") == 0)
!                 return crypt_verify(port, user, password);

              if (strcmp(crypt(password, test_pw), test_pw) == 0)
              {
                  /* it matched. */
--- 82,95 ----
               * the current code needs non-encrypted passwords to
               * encrypt with a random salt.
               */
!             if (port->auth_method == uaCrypt ||
!                 port->auth_method == uaMD5 ||
!                 test_pw == NULL ||
!                 test_pw[0] == '\0' ||
!                 strcmp(test_pw, "+") == 0)
!                 return md5_crypt_verify(port, user, password);

+             /* external password file is crypt-only */
              if (strcmp(crypt(password, test_pw), test_pw) == 0)
              {
                  /* it matched. */
Index: src/backend/libpq/pg_hba.conf.sample
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/pg_hba.conf.sample,v
retrieving revision 1.23
diff -c -r1.23 pg_hba.conf.sample
*** src/backend/libpq/pg_hba.conf.sample    2001/08/01 23:25:39    1.23
--- src/backend/libpq/pg_hba.conf.sample    2001/08/15 05:12:55
***************
*** 3,9 ****
  #
  #
  # This file controls:
- #
  #     o which hosts are allowed to connect
  #     o how users are authenticated on each host
  #     o databases accessible by each host
--- 3,8 ----
***************
*** 24,30 ****
  # ============
  #
  # There are three types of records:
- #
  #     o host
  #     o hostssl
  #     o local
--- 23,28 ----
***************
*** 40,46 ****
  #   host  DBNAME  IP_ADDRESS  ADDRESS_MASK  AUTH_TYPE  [AUTH_ARGUMENT]
  #
  # DBNAME can be:
- #
  #     o the name of a PostgreSQL database
  #     o "all" to indicate all databases
  #     o "sameuser" to allow access only to databases with the same
--- 38,43 ----
***************
*** 80,86 ****
  # allowed only if this record type appears.
  #
  # Format:
- #
  #   local  DBNAME  AUTH_TYPE  [AUTH_ARGUMENT]
  #
  # This format is identical to the "host" record type except the IP_ADDRESS
--- 77,82 ----
***************
*** 219,222 ****

  local      all                                          trust
  host       all         127.0.0.1     255.255.255.255    trust
-
--- 215,217 ----
Index: src/backend/parser/gram.y
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.244
diff -c -r2.244 gram.y
*** src/backend/parser/gram.y    2001/08/13 21:34:51    2.244
--- src/backend/parser/gram.y    2001/08/15 05:13:01
***************
*** 306,312 ****
          CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR,
          DAY_P, DEC, DECIMAL, DECLARE, DEFAULT, DELETE, DESC,
          DISTINCT, DOUBLE, DROP,
!         ELSE, END_TRANS, ESCAPE, EXCEPT, EXECUTE, EXISTS, EXTRACT,
          FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
          GLOBAL, GRANT, GROUP, HAVING, HOUR_P,
          IN, INNER_P, INSENSITIVE, INSERT, INTERSECT, INTERVAL, INTO, IS,
--- 306,312 ----
          CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR,
          DAY_P, DEC, DECIMAL, DECLARE, DEFAULT, DELETE, DESC,
          DISTINCT, DOUBLE, DROP,
!         ELSE, ENCRYPTED, END_TRANS, ESCAPE, EXCEPT, EXECUTE, EXISTS, EXTRACT,
          FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
          GLOBAL, GRANT, GROUP, HAVING, HOUR_P,
          IN, INNER_P, INSENSITIVE, INSERT, INTERSECT, INTERVAL, INTO, IS,
***************
*** 319,325 ****
          SCHEMA, SCROLL, SECOND_P, SELECT, SESSION, SESSION_USER, SET, SOME, SUBSTRING,
          TABLE, TEMPORARY, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR,
          TIMEZONE_MINUTE, TO, TRAILING, TRANSACTION, TRIM, TRUE_P,
!         UNION, UNIQUE, UNKNOWN, UPDATE, USER, USING,
          VALUES, VARCHAR, VARYING, VIEW,
          WHEN, WHERE, WITH, WORK, YEAR_P, ZONE

--- 319,325 ----
          SCHEMA, SCROLL, SECOND_P, SELECT, SESSION, SESSION_USER, SET, SOME, SUBSTRING,
          TABLE, TEMPORARY, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR,
          TIMEZONE_MINUTE, TO, TRAILING, TRANSACTION, TRIM, TRUE_P,
!         UNENCRYPTED, UNION, UNIQUE, UNKNOWN, UPDATE, USER, USING,
          VALUES, VARCHAR, VARYING, VIEW,
          WHEN, WHERE, WITH, WORK, YEAR_P, ZONE

***************
*** 558,563 ****
--- 558,575 ----
                    $$->defname = "password";
                    $$->arg = (Node *)makeString($2);
                  }
+               | ENCRYPTED PASSWORD Sconst
+                 {
+                   $$ = makeNode(DefElem);
+                   $$->defname = "encryptedPassword";
+                   $$->arg = (Node *)makeString($3);
+                 }
+               | UNENCRYPTED PASSWORD Sconst
+                 {
+                   $$ = makeNode(DefElem);
+                   $$->defname = "unencryptedPassword";
+                   $$->arg = (Node *)makeString($3);
+                 }
                | SYSID Iconst
                  {
                    $$ = makeNode(DefElem);
***************
*** 5765,5770 ****
--- 5777,5783 ----
          | DISTINCT                        { $$ = "distinct"; }
          | DO                            { $$ = "do"; }
          | ELSE                            { $$ = "else"; }
+         | ENCRYPTED                        { $$ = "encrypted"; }
          | END_TRANS                        { $$ = "end"; }
          | EXCEPT                        { $$ = "except"; }
          | EXISTS                        { $$ = "exists"; }
***************
*** 5836,5841 ****
--- 5849,5855 ----
          | TRANSACTION                    { $$ = "transaction"; }
          | TRIM                            { $$ = "trim"; }
          | TRUE_P                        { $$ = "true"; }
+         | UNENCRYPTED                    { $$ = "unencrypted"; }
          | UNION                            { $$ = "union"; }
          | UNIQUE                        { $$ = "unique"; }
          | UNKNOWN                        { $$ = "unknown"; }
Index: src/backend/parser/keywords.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/parser/keywords.c,v
retrieving revision 1.94
diff -c -r1.94 keywords.c
*** src/backend/parser/keywords.c    2001/07/16 05:06:58    1.94
--- src/backend/parser/keywords.c    2001/08/15 05:13:02
***************
*** 102,107 ****
--- 102,108 ----
      {"each", EACH},
      {"else", ELSE},
      {"encoding", ENCODING},
+     {"encrypted", ENCRYPTED},
      {"end", END_TRANS},
      {"escape", ESCAPE},
      {"except", EXCEPT},
***************
*** 262,267 ****
--- 263,269 ----
      {"truncate", TRUNCATE},
      {"trusted", TRUSTED},
      {"type", TYPE_P},
+     {"unencrypted", UNENCRYPTED},
      {"union", UNION},
      {"unique", UNIQUE},
      {"unknown", UNKNOWN},
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/utils/misc/guc.c,v
retrieving revision 1.45
diff -c -r1.45 guc.c
*** src/backend/utils/misc/guc.c    2001/07/05 15:19:40    1.45
--- src/backend/utils/misc/guc.c    2001/08/15 05:13:03
***************
*** 80,85 ****
--- 80,87 ----

  bool        Australian_timezones = false;

+ bool        Password_encryption = false;
+
  #ifndef PG_KRB_SRVTAB
  #define PG_KRB_SRVTAB ""
  #endif
***************
*** 246,255 ****

      {"sql_inheritance", PGC_USERSET, &SQL_inheritance, true, NULL},

!     {"australian_timezones", PGC_USERSET, &Australian_timezones,
!     false, ClearDateCache},

      {"fixbtree", PGC_POSTMASTER, &FixBTree, true, NULL},

      {NULL, 0, NULL, false, NULL}
  };
--- 248,258 ----

      {"sql_inheritance", PGC_USERSET, &SQL_inheritance, true, NULL},

!     {"australian_timezones", PGC_USERSET, &Australian_timezones, false, ClearDateCache},

      {"fixbtree", PGC_POSTMASTER, &FixBTree, true, NULL},
+
+     {"password_encryption", PGC_USERSET, &Password_encryption, false, NULL},

      {NULL, 0, NULL, false, NULL}
  };
Index: src/backend/utils/misc/postgresql.conf.sample
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/utils/misc/postgresql.conf.sample,v
retrieving revision 1.15
diff -c -r1.15 postgresql.conf.sample
*** src/backend/utils/misc/postgresql.conf.sample    2001/07/05 15:19:40    1.15
--- src/backend/utils/misc/postgresql.conf.sample    2001/08/15 05:13:03
***************
*** 176,184 ****
  #
  #    Misc
  #
- #default_transaction_isolation = 'read committed'
- #sql_inheritance = true
  #australian_timezones = false
  #deadlock_timeout = 1000
  #max_expr_depth = 10000 # min 10

--- 176,185 ----
  #
  #    Misc
  #
  #australian_timezones = false
  #deadlock_timeout = 1000
+ #default_transaction_isolation = 'read committed'
  #max_expr_depth = 10000 # min 10
+ #password_encryption = false
+ #sql_inheritance = true

Index: src/include/miscadmin.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/miscadmin.h,v
retrieving revision 1.88
diff -c -r1.88 miscadmin.h
*** src/include/miscadmin.h    2001/08/05 02:06:50    1.88
--- src/include/miscadmin.h    2001/08/15 05:13:03
***************
*** 171,178 ****
  extern bool allowSystemTableMods;
  extern int    SortMem;

! /* a few postmaster startup options are exported here so the
!    configuration file processor has access to them */

  extern bool NetServer;
  extern bool EnableSSL;
--- 171,180 ----
  extern bool allowSystemTableMods;
  extern int    SortMem;

! /*
!  *    A few postmaster startup options are exported here so the
!  *  configuration file processor can access them.
!  */

  extern bool NetServer;
  extern bool EnableSSL;
Index: src/include/libpq/crypt.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/libpq/crypt.h,v
retrieving revision 1.11
diff -c -r1.11 crypt.h
*** src/include/libpq/crypt.h    2000/07/04 16:32:01    1.11
--- src/include/libpq/crypt.h    2001/08/15 05:13:03
***************
*** 22,31 ****
  extern char *crypt_getpwdfilename(void);
  extern char *crypt_getpwdreloadfilename(void);

! #ifdef NOT_USED
! extern MsgType crypt_salt(const char *user);
!
! #endif
! extern int    crypt_verify(const Port *port, const char *user, const char *pgpass);

  #endif
--- 22,27 ----
  extern char *crypt_getpwdfilename(void);
  extern char *crypt_getpwdreloadfilename(void);

! extern int md5_crypt_verify(const Port *port, const char *user, const char *pgpass);

  #endif
Index: src/include/libpq/hba.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/libpq/hba.h,v
retrieving revision 1.22
diff -c -r1.22 hba.h
*** src/include/libpq/hba.h    2001/08/01 23:25:39    1.22
--- src/include/libpq/hba.h    2001/08/15 05:13:03
***************
*** 35,41 ****
      uaTrust,
      uaIdent,
      uaPassword,
!     uaCrypt
  } UserAuth;

  typedef struct Port hbaPort;
--- 35,43 ----
      uaTrust,
      uaIdent,
      uaPassword,
!     uaCrypt,
!     uaMD5        /*     This starts as uaCrypt from pg_hba.conf, but gets
!                     overridden if the client supports MD5 */
  } UserAuth;

  typedef struct Port hbaPort;
Index: src/include/libpq/pqcomm.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/libpq/pqcomm.h,v
retrieving revision 1.55
diff -c -r1.55 pqcomm.h
*** src/include/libpq/pqcomm.h    2001/03/22 04:00:48    1.55
--- src/include/libpq/pqcomm.h    2001/08/15 05:13:05
***************
*** 90,96 ****
  /* The earliest and latest frontend/backend protocol version supported. */

  #define PG_PROTOCOL_EARLIEST    PG_PROTOCOL(0,0)
! #define PG_PROTOCOL_LATEST    PG_PROTOCOL(2,0)

  /*
   * All packets sent to the postmaster start with the length.  This is omitted
--- 90,96 ----
  /* The earliest and latest frontend/backend protocol version supported. */

  #define PG_PROTOCOL_EARLIEST    PG_PROTOCOL(0,0)
! #define PG_PROTOCOL_LATEST    PG_PROTOCOL(2,1)

  /*
   * All packets sent to the postmaster start with the length.  This is omitted
***************
*** 127,137 ****

  /* These are the authentication requests sent by the backend. */

! #define AUTH_REQ_OK        0        /* User is authenticated  */
  #define AUTH_REQ_KRB4        1    /* Kerberos V4 */
  #define AUTH_REQ_KRB5        2    /* Kerberos V5 */
  #define AUTH_REQ_PASSWORD    3    /* Password */
! #define AUTH_REQ_CRYPT        4    /* Encrypted password */

  typedef uint32 AuthRequest;

--- 127,138 ----

  /* These are the authentication requests sent by the backend. */

! #define AUTH_REQ_OK            0    /* User is authenticated  */
  #define AUTH_REQ_KRB4        1    /* Kerberos V4 */
  #define AUTH_REQ_KRB5        2    /* Kerberos V5 */
  #define AUTH_REQ_PASSWORD    3    /* Password */
! #define AUTH_REQ_CRYPT        4    /* crypt password */
! #define AUTH_REQ_MD5        5    /* md5 password */

  typedef uint32 AuthRequest;

Index: src/interfaces/libpq/Makefile
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/Makefile,v
retrieving revision 1.53
diff -c -r1.53 Makefile
*** src/interfaces/libpq/Makefile    2001/07/15 13:45:04    1.53
--- src/interfaces/libpq/Makefile    2001/08/15 05:13:05
***************
*** 20,26 ****
  override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) -DFRONTEND -DSYSCONFDIR='"$(sysconfdir)"'

  OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
!       pqexpbuffer.o dllist.o pqsignal.o \
        $(INET_ATON) $(SNPRINTF) $(STRERROR)

  ifdef MULTIBYTE
--- 20,26 ----
  override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) -DFRONTEND -DSYSCONFDIR='"$(sysconfdir)"'

  OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
!       pqexpbuffer.o dllist.o md5.o pqsignal.o \
        $(INET_ATON) $(SNPRINTF) $(STRERROR)

  ifdef MULTIBYTE
***************
*** 33,39 ****
  SHLIB_LINK += $(filter -L%, $(LDFLAGS)) $(filter -lcrypt -ldes -lkrb -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl
-lsocket-lnsl -lresolv -lintl, $(LIBS)) 


! all: all-lib

  # Shared library stuff
  include $(top_srcdir)/src/Makefile.shlib
--- 33,39 ----
  SHLIB_LINK += $(filter -L%, $(LDFLAGS)) $(filter -lcrypt -ldes -lkrb -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl
-lsocket-lnsl -lresolv -lintl, $(LIBS)) 


! all: md5.c md5.h all-lib

  # Shared library stuff
  include $(top_srcdir)/src/Makefile.shlib
***************
*** 49,54 ****
--- 49,60 ----
  dllist.c: $(backend_src)/lib/dllist.c
      rm -f $@ && $(LN_S) $< .

+ md5.c: $(backend_src)/libpq/md5.c
+     rm -f $@ && $(LN_S) $< .
+
+ md5.h: $(backend_src)/../include/libpq/md5.h
+     rm -f $@ && $(LN_S) $< .
+
  # this only gets done if configure finds system doesn't have inet_aton()
  inet_aton.c: $(backend_src)/port/inet_aton.c
      rm -f $@ && $(LN_S) $< .
***************
*** 82,88 ****
      rm -f $(addprefix $(DESTDIR)$(includedir)/, libpq-fe.h libpq-int.h pqexpbuffer.h)

  clean distclean maintainer-clean: clean-lib
!     rm -f $(OBJS) dllist.c wchar.c
      rm -f $(OBJS) inet_aton.c snprintf.c strerror.c

  depend dep:
--- 88,94 ----
      rm -f $(addprefix $(DESTDIR)$(includedir)/, libpq-fe.h libpq-int.h pqexpbuffer.h)

  clean distclean maintainer-clean: clean-lib
!     rm -f $(OBJS) dllist.c md5.c md5.h wchar.c
      rm -f $(OBJS) inet_aton.c snprintf.c strerror.c

  depend dep:
Index: src/interfaces/libpq/fe-auth.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v
retrieving revision 1.48
diff -c -r1.48 fe-auth.c
*** src/interfaces/libpq/fe-auth.c    2001/07/15 13:45:04    1.48
--- src/interfaces/libpq/fe-auth.c    2001/08/15 05:13:05
***************
*** 33,38 ****
--- 33,39 ----
  #include "libpq-fe.h"
  #include "libpq-int.h"
  #include "fe-auth.h"
+ #include "md5.h"

  #ifdef WIN32
  #include "win32.h"
***************
*** 434,445 ****
  static int
  pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
  {
      /* Encrypt the password if needed. */

!     if (areq == AUTH_REQ_CRYPT)
!         password = crypt(password, conn->salt);

!     return pqPacketSend(conn, password, strlen(password) + 1);
  }

  /*
--- 435,486 ----
  static int
  pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
  {
+     int ret;
+     char *crypt_pwd;
+
      /* Encrypt the password if needed. */

!     switch (areq)
!     {
!         case AUTH_REQ_CRYPT:
!             crypt_pwd = crypt(password, conn->salt);
!             break;
!         case AUTH_REQ_MD5:
!             {
!                 char *crypt_pwd2;

!                 if (!(crypt_pwd = malloc(MD5_PASSWD_LEN+1)) ||
!                     !(crypt_pwd2 = malloc(MD5_PASSWD_LEN+1)))
!                 {
!                     perror("malloc");
!                     return STATUS_ERROR;
!                 }
!                 if (!EncryptMD5(password, conn->pguser, crypt_pwd2))
!                 {
!                     free(crypt_pwd);
!                     free(crypt_pwd2);
!                     return STATUS_ERROR;
!                 }
!                 if (!EncryptMD5(crypt_pwd2 + strlen("md5"), conn->salt,
!                                 crypt_pwd))
!                 {
!                     free(crypt_pwd);
!                     free(crypt_pwd2);
!                     return STATUS_ERROR;
!                 }
!                 free(crypt_pwd2);
!                 break;
!             }
!         default:
!             /* discard const so we can assign it */
!             crypt_pwd = (char *)password;
!             break;
!     }
!
!     ret = pqPacketSend(conn, crypt_pwd, strlen(crypt_pwd) + 1);
!     if (areq == AUTH_REQ_MD5)
!         free(crypt_pwd);
!     return ret;
  }

  /*
***************
*** 494,499 ****
--- 535,541 ----

          case AUTH_REQ_PASSWORD:
          case AUTH_REQ_CRYPT:
+         case AUTH_REQ_MD5:
              if (password == NULL || *password == '\0')
              {
                  (void) sprintf(PQerrormsg,
***************
*** 506,514 ****
                   "fe_sendauth: error sending password authentication\n");
                  return STATUS_ERROR;
              }
-
              break;
-
          default:
              snprintf(PQerrormsg, PQERRORMSG_LENGTH,
                       libpq_gettext("authentication method %u not supported\n"), areq);
--- 548,554 ----
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.172
diff -c -r1.172 fe-connect.c
*** src/interfaces/libpq/fe-connect.c    2001/08/03 22:11:39    1.172
--- src/interfaces/libpq/fe-connect.c    2001/08/15 05:13:06
***************
*** 1341,1347 ****
                  }

                  /* Get the password salt if there is one. */
!                 if (areq == AUTH_REQ_CRYPT)
                  {
                      if (pqGetnchar(conn->salt, sizeof(conn->salt), conn))
                      {
--- 1341,1347 ----
                  }

                  /* Get the password salt if there is one. */
!                 if (areq == AUTH_REQ_CRYPT || areq == AUTH_REQ_MD5)
                  {
                      if (pqGetnchar(conn->salt, sizeof(conn->salt), conn))
                      {
***************
*** 1960,1966 ****
  closePGconn(PGconn *conn)
  {
      /* Note that the protocol doesn't allow us to send Terminate
!        messages during the startup phase. */
      if (conn->sock >= 0 && conn->status == CONNECTION_OK)
      {

--- 1960,1966 ----
  closePGconn(PGconn *conn)
  {
      /* Note that the protocol doesn't allow us to send Terminate
!        messages during the startup phase. */
      if (conn->sock >= 0 && conn->status == CONNECTION_OK)
      {

Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.105
diff -c -r1.105 fe-exec.c
*** src/interfaces/libpq/fe-exec.c    2001/08/03 22:11:39    1.105
--- src/interfaces/libpq/fe-exec.c    2001/08/15 05:13:13
***************
*** 1269,1275 ****
  static int
  getNotice(PGconn *conn)
  {
-
      /*
       * Since the Notice might be pretty long, we create a temporary
       * PQExpBuffer rather than using conn->workBuffer.    workBuffer is
--- 1269,1274 ----
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.36
diff -c -r1.36 libpq-int.h
*** src/interfaces/libpq/libpq-int.h    2001/07/15 13:45:04    1.36
--- src/interfaces/libpq/libpq-int.h    2001/08/15 05:13:13
***************
*** 45,51 ****
   * pqcomm.h describe what the backend knows, not what libpq knows.
   */

! #define PG_PROTOCOL_LIBPQ    PG_PROTOCOL(2,0)

  /*
   * POSTGRES backend dependent Constants.
--- 45,51 ----
   * pqcomm.h describe what the backend knows, not what libpq knows.
   */

! #define PG_PROTOCOL_LIBPQ    PG_PROTOCOL(2,1)

  /*
   * POSTGRES backend dependent Constants.
Index: src/interfaces/odbc/connection.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/odbc/connection.c,v
retrieving revision 1.32
diff -c -r1.32 connection.c
*** src/interfaces/odbc/connection.c    2001/06/19 02:17:06    1.32
--- src/interfaces/odbc/connection.c    2001/08/15 05:13:14
***************
*** 677,683 ****
                          mylog("auth got 'R'\n");

                          areq = SOCK_get_int(sock, 4);
!                         if (areq == AUTH_REQ_CRYPT)
                              SOCK_get_n_char(sock, salt, 2);

                          mylog("areq = %d\n", areq);
--- 677,683 ----
                          mylog("auth got 'R'\n");

                          areq = SOCK_get_int(sock, 4);
!                         if (areq == AUTH_REQ_CRYPT || areq == AUTH_REQ_MD5)
                              SOCK_get_n_char(sock, salt, 2);

                          mylog("areq = %d\n", areq);
***************
*** 717,722 ****
--- 717,723 ----
                              break;

                          case AUTH_REQ_CRYPT:
+                         case AUTH_REQ_MD5:
                              self->errormsg = "Password crypt authentication not supported";
                              self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED;
                              return 0;
***************
*** 1672,1686 ****

  int     CC_get_max_query_len(const ConnectionClass *conn)
  {
!         int     value;
!         /* Long Queries in 7.0+ */
!         if (PG_VERSION_GE(conn, 7.0))
!                 value = 0 /* MAX_STATEMENT_LEN */;
!         /* Prior to 7.0 we used 2*BLCKSZ */
!         else if (PG_VERSION_GE(conn, 6.5))
!                 value = (2 * BLCKSZ);
!         else
!                 /* Prior to 6.5 we used BLCKSZ */
!                 value = BLCKSZ;
!         return value;
  }
--- 1673,1687 ----

  int     CC_get_max_query_len(const ConnectionClass *conn)
  {
!         int     value;
!         /* Long Queries in 7.0+ */
!         if (PG_VERSION_GE(conn, 7.0))
!                 value = 0 /* MAX_STATEMENT_LEN */;
!         /* Prior to 7.0 we used 2*BLCKSZ */
!         else if (PG_VERSION_GE(conn, 6.5))
!                 value = (2 * BLCKSZ);
!         else
!                 /* Prior to 6.5 we used BLCKSZ */
!                 value = BLCKSZ;
!         return value;
  }
Index: src/interfaces/odbc/connection.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/odbc/connection.h,v
retrieving revision 1.24
diff -c -r1.24 connection.h
*** src/interfaces/odbc/connection.h    2001/05/30 20:52:34    1.24
--- src/interfaces/odbc/connection.h    2001/08/15 05:13:14
***************
*** 93,98 ****
--- 93,99 ----
  #define AUTH_REQ_KRB5                                2
  #define AUTH_REQ_PASSWORD                            3
  #define AUTH_REQ_CRYPT                                4
+ #define AUTH_REQ_MD5                                5

  /*    Startup Packet sizes */
  #define SM_DATABASE                                    64
/*
 *  md5.c
 *
 *  Implements  the  MD5 Message-Digest Algorithm as specified in
 *  RFC  1321.  This  implementation  is a simple one, in that it
 *  needs  every  input  byte  to  be  buffered  before doing any
 *  calculations.  I  do  not  expect  this  file  to be used for
 *  general  purpose  MD5'ing  of large amounts of data, only for
 *  generating hashed passwords from limited input.
 *
 *  Sverre H. Huseby <sverrehu@online.no>
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "postgres.h"
#include "libpq/md5.h"

/*
 *    PRIVATE FUNCTIONS
 */

typedef unsigned char unsigned8;
typedef unsigned int  unsigned32;
typedef unsigned long unsigned64;

/*
 *    The returned array is allocated using malloc.  the caller should free it
 *     when it is no longer needed.
 */
static unsigned8 *
createPaddedCopyWithLength(unsigned8 *b, unsigned32 *l)
{
    unsigned8  *ret;
    unsigned32 q;
    unsigned32 len, newLen448;
    unsigned64 len64;

    len = ((b == NULL) ? 0 : *l);
    newLen448 = len + 64 - (len % 64) - 8;
    if (newLen448 <= len)
        newLen448 += 64;

    *l = newLen448 + 8;
    if ((ret = (unsigned8 *) malloc(sizeof(unsigned8) * *l)) == NULL)
        return NULL;

    if (b != NULL)
        memcpy(ret, b, sizeof(unsigned8) * len);

    /* pad */
    ret[len] = 0x80;
    for (q = len + 1; q < newLen448; q++)
        ret[q] = 0x00;

    /* append length as a 64 bit bitcount */
    len64 = len;
    len64 <<= 3;
    q = newLen448;
    ret[q++] = (len64 & 0xFF);
    len64 >>= 8;
    ret[q++] = (len64 & 0xFF);
    len64 >>= 8;
    ret[q++] = (len64 & 0xFF);
    len64 >>= 8;
    ret[q++] = (len64 & 0xFF);
    len64 >>= 8;
    ret[q++] = (len64 & 0xFF);
    len64 >>= 8;
    ret[q++] = (len64 & 0xFF);
    len64 >>= 8;
    ret[q++] = (len64 & 0xFF);
    len64 >>= 8;
    ret[q] = (len64 & 0xFF);

    return ret;
}

#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define ROT_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))

static void
doTheRounds(unsigned32 X[16], unsigned32 state[4])
{
    unsigned32 a, b, c, d;

    a = state[0];
    b = state[1];
    c = state[2];
    d = state[3];

    /* round 1 */
    a = b + ROT_LEFT((a + F(b, c, d) + X[ 0] + 0xd76aa478),  7);  /*  1 */
    d = a + ROT_LEFT((d + F(a, b, c) + X[ 1] + 0xe8c7b756), 12);  /*  2 */
    c = d + ROT_LEFT((c + F(d, a, b) + X[ 2] + 0x242070db), 17);  /*  3 */
    b = c + ROT_LEFT((b + F(c, d, a) + X[ 3] + 0xc1bdceee), 22);  /*  4 */
    a = b + ROT_LEFT((a + F(b, c, d) + X[ 4] + 0xf57c0faf),  7);  /*  5 */
    d = a + ROT_LEFT((d + F(a, b, c) + X[ 5] + 0x4787c62a), 12);  /*  6 */
    c = d + ROT_LEFT((c + F(d, a, b) + X[ 6] + 0xa8304613), 17);  /*  7 */
    b = c + ROT_LEFT((b + F(c, d, a) + X[ 7] + 0xfd469501), 22);  /*  8 */
    a = b + ROT_LEFT((a + F(b, c, d) + X[ 8] + 0x698098d8),  7);  /*  9 */
    d = a + ROT_LEFT((d + F(a, b, c) + X[ 9] + 0x8b44f7af), 12);  /* 10 */
    c = d + ROT_LEFT((c + F(d, a, b) + X[10] + 0xffff5bb1), 17);  /* 11 */
    b = c + ROT_LEFT((b + F(c, d, a) + X[11] + 0x895cd7be), 22);  /* 12 */
    a = b + ROT_LEFT((a + F(b, c, d) + X[12] + 0x6b901122),  7);  /* 13 */
    d = a + ROT_LEFT((d + F(a, b, c) + X[13] + 0xfd987193), 12);  /* 14 */
    c = d + ROT_LEFT((c + F(d, a, b) + X[14] + 0xa679438e), 17);  /* 15 */
    b = c + ROT_LEFT((b + F(c, d, a) + X[15] + 0x49b40821), 22);  /* 16 */

    /* round 2 */
    a = b + ROT_LEFT((a + G(b, c, d) + X[ 1] + 0xf61e2562),  5);  /* 17 */
    d = a + ROT_LEFT((d + G(a, b, c) + X[ 6] + 0xc040b340),  9);  /* 18 */
    c = d + ROT_LEFT((c + G(d, a, b) + X[11] + 0x265e5a51), 14);  /* 19 */
    b = c + ROT_LEFT((b + G(c, d, a) + X[ 0] + 0xe9b6c7aa), 20);  /* 20 */
    a = b + ROT_LEFT((a + G(b, c, d) + X[ 5] + 0xd62f105d),  5);  /* 21 */
    d = a + ROT_LEFT((d + G(a, b, c) + X[10] + 0x02441453),  9);  /* 22 */
    c = d + ROT_LEFT((c + G(d, a, b) + X[15] + 0xd8a1e681), 14);  /* 23 */
    b = c + ROT_LEFT((b + G(c, d, a) + X[ 4] + 0xe7d3fbc8), 20);  /* 24 */
    a = b + ROT_LEFT((a + G(b, c, d) + X[ 9] + 0x21e1cde6),  5);  /* 25 */
    d = a + ROT_LEFT((d + G(a, b, c) + X[14] + 0xc33707d6),  9);  /* 26 */
    c = d + ROT_LEFT((c + G(d, a, b) + X[ 3] + 0xf4d50d87), 14);  /* 27 */
    b = c + ROT_LEFT((b + G(c, d, a) + X[ 8] + 0x455a14ed), 20);  /* 28 */
    a = b + ROT_LEFT((a + G(b, c, d) + X[13] + 0xa9e3e905),  5);  /* 29 */
    d = a + ROT_LEFT((d + G(a, b, c) + X[ 2] + 0xfcefa3f8),  9);  /* 30 */
    c = d + ROT_LEFT((c + G(d, a, b) + X[ 7] + 0x676f02d9), 14);  /* 31 */
    b = c + ROT_LEFT((b + G(c, d, a) + X[12] + 0x8d2a4c8a), 20);  /* 32 */

    /* round 3 */
    a = b + ROT_LEFT((a + H(b, c, d) + X[ 5] + 0xfffa3942),  4);  /* 33 */
    d = a + ROT_LEFT((d + H(a, b, c) + X[ 8] + 0x8771f681), 11);  /* 34 */
    c = d + ROT_LEFT((c + H(d, a, b) + X[11] + 0x6d9d6122), 16);  /* 35 */
    b = c + ROT_LEFT((b + H(c, d, a) + X[14] + 0xfde5380c), 23);  /* 36 */
    a = b + ROT_LEFT((a + H(b, c, d) + X[ 1] + 0xa4beea44),  4);  /* 37 */
    d = a + ROT_LEFT((d + H(a, b, c) + X[ 4] + 0x4bdecfa9), 11);  /* 38 */
    c = d + ROT_LEFT((c + H(d, a, b) + X[ 7] + 0xf6bb4b60), 16);  /* 39 */
    b = c + ROT_LEFT((b + H(c, d, a) + X[10] + 0xbebfbc70), 23);  /* 40 */
    a = b + ROT_LEFT((a + H(b, c, d) + X[13] + 0x289b7ec6),  4);  /* 41 */
    d = a + ROT_LEFT((d + H(a, b, c) + X[ 0] + 0xeaa127fa), 11);  /* 42 */
    c = d + ROT_LEFT((c + H(d, a, b) + X[ 3] + 0xd4ef3085), 16);  /* 43 */
    b = c + ROT_LEFT((b + H(c, d, a) + X[ 6] + 0x04881d05), 23);  /* 44 */
    a = b + ROT_LEFT((a + H(b, c, d) + X[ 9] + 0xd9d4d039),  4);  /* 45 */
    d = a + ROT_LEFT((d + H(a, b, c) + X[12] + 0xe6db99e5), 11);  /* 46 */
    c = d + ROT_LEFT((c + H(d, a, b) + X[15] + 0x1fa27cf8), 16);  /* 47 */
    b = c + ROT_LEFT((b + H(c, d, a) + X[ 2] + 0xc4ac5665), 23);  /* 48 */

    /* round 4 */
    a = b + ROT_LEFT((a + I(b, c, d) + X[ 0] + 0xf4292244),  6);  /* 49 */
    d = a + ROT_LEFT((d + I(a, b, c) + X[ 7] + 0x432aff97), 10);  /* 50 */
    c = d + ROT_LEFT((c + I(d, a, b) + X[14] + 0xab9423a7), 15);  /* 51 */
    b = c + ROT_LEFT((b + I(c, d, a) + X[ 5] + 0xfc93a039), 21);  /* 52 */
    a = b + ROT_LEFT((a + I(b, c, d) + X[12] + 0x655b59c3),  6);  /* 53 */
    d = a + ROT_LEFT((d + I(a, b, c) + X[ 3] + 0x8f0ccc92), 10);  /* 54 */
    c = d + ROT_LEFT((c + I(d, a, b) + X[10] + 0xffeff47d), 15);  /* 55 */
    b = c + ROT_LEFT((b + I(c, d, a) + X[ 1] + 0x85845dd1), 21);  /* 56 */
    a = b + ROT_LEFT((a + I(b, c, d) + X[ 8] + 0x6fa87e4f),  6);  /* 57 */
    d = a + ROT_LEFT((d + I(a, b, c) + X[15] + 0xfe2ce6e0), 10);  /* 58 */
    c = d + ROT_LEFT((c + I(d, a, b) + X[ 6] + 0xa3014314), 15);  /* 59 */
    b = c + ROT_LEFT((b + I(c, d, a) + X[13] + 0x4e0811a1), 21);  /* 60 */
    a = b + ROT_LEFT((a + I(b, c, d) + X[ 4] + 0xf7537e82),  6);  /* 61 */
    d = a + ROT_LEFT((d + I(a, b, c) + X[11] + 0xbd3af235), 10);  /* 62 */
    c = d + ROT_LEFT((c + I(d, a, b) + X[ 2] + 0x2ad7d2bb), 15);  /* 63 */
    b = c + ROT_LEFT((b + I(c, d, a) + X[ 9] + 0xeb86d391), 21);  /* 64 */

    state[0] += a;
    state[1] += b;
    state[2] += c;
    state[3] += d;
}

static int
calculateDigestFromBuffer(unsigned8 *b, unsigned32 len, unsigned8 sum[16])
{
    register unsigned32 i, j, k, newI;
    unsigned32 l;
    unsigned8 *input;
    register unsigned32 *wbp;
    unsigned32 workBuff[16], state[4];

    l = len;

    state[0] = 0x67452301;
    state[1] = 0xEFCDAB89;
    state[2] = 0x98BADCFE;
    state[3] = 0x10325476;

    if ((input = createPaddedCopyWithLength(b, &l)) == NULL)
        return 0;

    for (i = 0;;) {
        if ((newI = i + 16 * 4) > l)
            break;
        k = i + 3;
        for (j = 0; j < 16; j++) {
            wbp = (workBuff + j);
            *wbp = input[k--];
            *wbp <<= 8;
            *wbp |= input[k--];
            *wbp <<= 8;
            *wbp |= input[k--];
            *wbp <<= 8;
            *wbp |= input[k];
            k += 7;
        }
        doTheRounds(workBuff, state);
        i = newI;
    }
    free(input);

    j = 0;
    for (i = 0; i < 4; i++) {
        k = state[i];
        sum[j++] = (k & 0xFF);
        k >>= 8;
        sum[j++] = (k & 0xFF);
        k >>= 8;
        sum[j++] = (k & 0xFF);
        k >>= 8;
        sum[j++] = (k & 0xFF);
    }
    return 1;
}

static void
bytesToHex(unsigned8 b[16], char *s)
{
    static char *hex = "0123456789abcdef";
    int         q, w;

    for (q = 0, w = 0; q < 16; q++) {
        s[w++] = hex[(b[q] >> 4) & 0x0F];
        s[w++] = hex[b[q] & 0x0F];
    }
    s[w] = '\0';
}

/*
 *  PUBLIC FUNCTIONS
 */

/*
 *  md5_hash
 *
 *  Calculates the MD5 sum of the bytes in a buffer.
 *
 *  SYNOPSIS      #include "md5.h"
 *                int md5_hash(const void *buff, size_t len, char *hexsum)
 *
 *  INPUT         buff    the buffer containing the bytes that you want
 *                        the MD5 sum of.
 *                len     number of bytes in the buffer.
 *
 *  OUTPUT        hexsum  the MD5 sum as a '\0'-terminated string of
 *                        hexadecimal digits.  an MD5 sum is 16 bytes long.
 *                        each byte is represented by two heaxadecimal
 *                        characters.  you thus need to provide an array
 *                        of 33 characters, including the trailing '\0'.
 *
 *  RETURNS       0 on failure (out of memory for internal buffers) or
 *                non-zero on success.
 *
 *  STANDARDS     MD5 is described in RFC 1321.
 *
 *  AUTHOR        Sverre H. Huseby <sverrehu@online.no>
 *
 */
bool
md5_hash(const void *buff, size_t len, char *hexsum)
{
    unsigned8 sum[16];

    if (!calculateDigestFromBuffer((unsigned8 *) buff, len, sum))
        return false;

    bytesToHex(sum, hexsum);
    return true;
}



/*
 * puts  md5(username+passwd)  in buf provided buflen is at least 36 bytes
 * returns 1 on success, 0 on any kind of failure and sets errno accordingly
 */
bool EncryptMD5(const char *passwd, const char *salt, char *buf)
{
    char crypt_buf[128];

    if (strlen(salt) + strlen(passwd) > 127)
        return false;

    strcpy(buf, "md5");
    memset(crypt_buf, 0, 128);
    sprintf(crypt_buf,"%s%s", salt, passwd);

    return md5_hash(crypt_buf, strlen(crypt_buf), buf + 3);
}
/*-------------------------------------------------------------------------
 *
 * md5.h
 *      Interface to hba.c
 *
 *
 *-------------------------------------------------------------------------
 */
#ifndef PG_MD5_H
#define PG_MD5_H

extern bool md5_hash(const void *buff, size_t len, char *hexsum);
extern bool CheckMD5Pwd(char *passwd, char *storedpwd, char *seed);
extern bool EncryptMD5(const char *passwd, const char *salt, char *buf);

#define MD5_PASSWD_LEN    35

#define isMD5(passwd)    (strncmp((passwd),"md5",3) == 0 && \
                         strlen(passwd) == MD5_PASSWD_LEN)

#endif

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: Patch: use SCM_CREDS authentication over PF_LOCAL sockets
Next
From: Denis Perchine
Date:
Subject: Re: patch for JDBC PreparedStatement