Thread: Re: Proposal for encrypting pg_shadow passwords

Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
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

Re: Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
> 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().

One more issue, and that is salt.  The patch uses the username as salt
for storing in pg_shadow.  That is how Vince's code did it, and it
seemed OK to me.  It prevents me from having to send a second salt over
the wire, and it prevents me from having to pull the salt out of
pg_shadow so I can send it to the client.  Not sure if this is how
FreeBSD does it.

Also, we are using only two characters for salt right now because that
is all crypt() accepts.  Should we make it larger with MD5?  We can only
use printable characters, so we only have:

    > 62*62
    3844

I am not sure this is random enough to prevent possible playback.
Remember, this salt is used to prevent playback of over-the-wire
passwords.

--
  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

Re: Re: Proposal for encrypting pg_shadow passwords

From
Peter Eisentraut
Date:
Bruce Momjian writes:

>     two new files (backend/libpq/md5.c and include/libpq/md5.h)
>         which implement MD5 encryption (from Vince with cleanups)

How about adding these to backend/utils/adt, so we can provide an SQL md5
function, just for completeness.  (Might be useful to manually verify the
passwords in pg_shadow or something.)  There's already code for this in
contrib/pgcrypto, btw.

--
Peter Eisentraut   peter_e@gmx.net   http://funkturm.homeip.net/~peter


Re: Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
> 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)
>

Patch applied. I have to move on to SCM patch.

--
  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

Re: Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
> Bruce Momjian writes:
>
> >     two new files (backend/libpq/md5.c and include/libpq/md5.h)
> >         which implement MD5 encryption (from Vince with cleanups)
>
> How about adding these to backend/utils/adt, so we can provide an SQL md5
> function, just for completeness.  (Might be useful to manually verify the
> passwords in pg_shadow or something.)  There's already code for this in
> contrib/pgcrypto, btw.

Uh, yea, I guess I could do that.  However, I need to move on to the SCM
creditials patch if we want that in 7.2.  In fact, because the md5 patch
didn't make anyone ill, I am going to apply it now so I can start on the
SCM stuff.  I can't figure out how to keep them a separate patches
because they overlap so much.

Also, should we be adding it if it is already in pgcrypto with other
nice items?

--
  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

Re: Re: Proposal for encrypting pg_shadow passwords

From
Peter Eisentraut
Date:
Bruce Momjian writes:

> 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 also see that the md5.c implementation does not use palloc and makes the
rather odd assumption that long int == unsigned64.

What's your rush?  Is there a deadline now?

--
Peter Eisentraut   peter_e@gmx.net   http://funkturm.homeip.net/~peter


Re: Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
> Bruce Momjian writes:
>
> > 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 also see that the md5.c implementation does not use palloc and makes the
> rather odd assumption that long int == unsigned64.

That is from Vince's code, I think.  Can you suggest a fix?

> What's your rush?  Is there a deadline now?

I want to do SCM patch, then write presentation for LinuxWorld, and go
to Linuxworld. Then we are at the end of August.  Also, people need to
do the Java MD5 code, and if I want that before we start 7.2 beta, I
feel rushed.

--
  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

Re: Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
> > Bruce Momjian writes:
> >
> > > 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 also see that the md5.c implementation does not use palloc and makes the
> > rather odd assumption that long int == unsigned64.

Also, I didn't use palloc because the same C code is used in the backend
and libpq.

> That is from Vince's code, I think.  Can you suggest a fix?
>
> > What's your rush?  Is there a deadline now?
>
> I want to do SCM patch, then write presentation for LinuxWorld, and go
> to Linuxworld. Then we are at the end of August.  Also, people need to
> do the Java MD5 code, and if I want that before we start 7.2 beta, I
> feel rushed.

It is not knowing how quickly the ODBC/JDBC people can handle this that
has me a little worried.

--
  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

Re: Re: Proposal for encrypting pg_shadow passwords

From
Neil Padgett
Date:
Bruce Momjian wrote:
>
> > 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)
> >
>
> Patch applied. I have to move on to SCM patch.

Hmm... I used to be able to build in a separate tree than my source
tree. Since this patch was applied, I can't. It seems that the
preprocessor is finding the system md5.h (i.e. /usr/include/md5.h)
rather than the PostgreSQL md5.h. Suggestions on how to fix?

Neil

--
Neil Padgett
Red Hat Canada Ltd.                       E-Mail:  npadgett@redhat.com
2323 Yonge Street, Suite #300,
Toronto, ON  M4P 2C9

Re: Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
> Bruce Momjian wrote:
> >
> > > 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)
> > >
> >
> > Patch applied. I have to move on to SCM patch.
>
> Hmm... I used to be able to build in a separate tree than my source
> tree. Since this patch was applied, I can't. It seems that the
> preprocessor is finding the system md5.h (i.e. /usr/include/md5.h)
> rather than the PostgreSQL md5.h. Suggestions on how to fix?

Ewe, you have md5.h somewhere else.  Let me rename it to pgmd5.h.  Is
that OK for everyone?  Or maybe I should move it all into crypt.h.  That
seems to make more sense.

--
  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

Re: Re: Proposal for encrypting pg_shadow passwords

From
Peter Eisentraut
Date:
Bruce Momjian writes:

> > Bruce Momjian writes:
> >
> > > 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 also see that the md5.c implementation does not use palloc and makes the
> > rather odd assumption that long int == unsigned64.
>
> That is from Vince's code, I think.  Can you suggest a fix?

Remove that code and use contrib/pgcrypto/md5.{c.h}.  And move the .h file
to the include tree.

--
Peter Eisentraut   peter_e@gmx.net   http://funkturm.homeip.net/~peter


Re: Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
> Bruce Momjian wrote:
> >
> > > 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)
> > >
> >
> > Patch applied. I have to move on to SCM patch.
>
> Hmm... I used to be able to build in a separate tree than my source
> tree. Since this patch was applied, I can't. It seems that the
> preprocessor is finding the system md5.h (i.e. /usr/include/md5.h)
> rather than the PostgreSQL md5.h. Suggestions on how to fix?

OK, I move the contents of md5.h into crypt.h.  There wasn't much in
there anyway.

--
  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

Re: Re: Proposal for encrypting pg_shadow passwords

From
Peter Eisentraut
Date:
Bruce Momjian writes:

> > Hmm... I used to be able to build in a separate tree than my source
> > tree. Since this patch was applied, I can't. It seems that the
> > preprocessor is finding the system md5.h (i.e. /usr/include/md5.h)
> > rather than the PostgreSQL md5.h. Suggestions on how to fix?

I kind of doubt that, since the new code does #include "libpq/md5.h".

> Ewe, you have md5.h somewhere else.  Let me rename it to pgmd5.h.  Is
> that OK for everyone?  Or maybe I should move it all into crypt.h.  That
> seems to make more sense.

No, the problem is that you are not supposed to have include files in the
backend subtree.  I'm surprised the code builds at all.

--
Peter Eisentraut   peter_e@gmx.net   http://funkturm.homeip.net/~peter


Re: Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
> Bruce Momjian writes:
>
> > > Hmm... I used to be able to build in a separate tree than my source
> > > tree. Since this patch was applied, I can't. It seems that the
> > > preprocessor is finding the system md5.h (i.e. /usr/include/md5.h)
> > > rather than the PostgreSQL md5.h. Suggestions on how to fix?
>
> I kind of doubt that, since the new code does #include "libpq/md5.h".
>
> > Ewe, you have md5.h somewhere else.  Let me rename it to pgmd5.h.  Is
> > that OK for everyone?  Or maybe I should move it all into crypt.h.  That
> > seems to make more sense.
>
> No, the problem is that you are not supposed to have include files in the
> backend subtree.  I'm surprised the code builds at all.

Oh, yes, I had moved md5.h to libpq because I needed md5.c.  I should
have just pointed to libpq/md5.h.  Anyway, it is better in crypt.h
anyway.

--
  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

Re: Re: Proposal for encrypting pg_shadow passwords

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> In fact, because the md5 patch
> didn't make anyone ill,

The lack of objections may just mean that twelve hours wasn't sufficient
time for people to review it.

            regards, tom lane

Re: Re: Proposal for encrypting pg_shadow passwords

From
Peter Eisentraut
Date:
Bruce Momjian writes:

> > No, the problem is that you are not supposed to have include files in the
> > backend subtree.  I'm surprised the code builds at all.
>
> Oh, yes, I had moved md5.h to libpq because I needed md5.c.  I should
> have just pointed to libpq/md5.h.  Anyway, it is better in crypt.h
> anyway.

No, the solution is to move md5.h into include/libpq.

--
Peter Eisentraut   peter_e@gmx.net   http://funkturm.homeip.net/~peter


Re: Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
> Bruce Momjian writes:
>
> > > No, the problem is that you are not supposed to have include files in the
> > > backend subtree.  I'm surprised the code builds at all.
> >
> > Oh, yes, I had moved md5.h to libpq because I needed md5.c.  I should
> > have just pointed to libpq/md5.h.  Anyway, it is better in crypt.h
> > anyway.
>
> No, the solution is to move md5.h into include/libpq.

It was already in libpq.  I had just incorrectly copied it to libpq and
referenced it there.  You want a separate md5.h file?  Seems that may
conflict with OS md5.h files, or the md5.h file in contrib/pgcrypto.

--
  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

Re: Re: Proposal for encrypting pg_shadow passwords

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> Also, I didn't use palloc because the same C code is used in the backend
> and libpq.

"#define palloc(x) malloc(x)" has been our traditional solution to that.

What I'm more concerned about here is the blithe assumption that a
64-bit-int datatype is available.  I'm going through major pushups right
now to ensure that int8 sequences don't break machines without 64-bit
ints, and I'd like to see at least some minimal attention paid to the
issue in this code.

BTW, a protocol version bump for this is a horrid idea.  That will
create lots of compatibility problems for people, whether they use
the new auth mode or not.

            regards, tom lane

Re: Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > In fact, because the md5 patch
> > didn't make anyone ill,
>
> The lack of objections may just mean that twelve hours wasn't sufficient
> time for people to review it.

I am doing SCM now. If you want it backed out, I can do it.  I am not
making much headway in the SCM stuff anyway.

--
  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

Re: Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > Also, I didn't use palloc because the same C code is used in the backend
> > and libpq.
>
> "#define palloc(x) malloc(x)" has been our traditional solution to that.

OK, I copied ifdef's from pg_wchar.h.  That should fix it.  However,
Peter wants the pgcrypto md5 version, so that file is going away as soon
as I figure out how to use pgcrypto's md5 in its place.

> What I'm more concerned about here is the blithe assumption that a
> 64-bit-int datatype is available.  I'm going through major pushups right
> now to ensure that int8 sequences don't break machines without 64-bit
> ints, and I'd like to see at least some minimal attention paid to the
> issue in this code.

Again, going to be removed.

> BTW, a protocol version bump for this is a horrid idea.  That will
> create lots of compatibility problems for people, whether they use
> the new auth mode or not.

I thought protocol bumps were the way to handle such things.  The SCM
patch does it as well.  How should I tell if I am talking to a >=7.2
client?

--
  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

Re: Re: Proposal for encrypting pg_shadow passwords

From
Vince Vielhaber
Date:
On Wed, 15 Aug 2001, Bruce Momjian wrote:

> > Bruce Momjian writes:
> >
> > > 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 also see that the md5.c implementation does not use palloc and makes the
> > rather odd assumption that long int == unsigned64.
>
> That is from Vince's code, I think.  Can you suggest a fix?
>
> > What's your rush?  Is there a deadline now?
>
> I want to do SCM patch, then write presentation for LinuxWorld, and go
> to Linuxworld. Then we are at the end of August.  Also, people need to
> do the Java MD5 code, and if I want that before we start 7.2 beta, I
> feel rushed.

Interesting... When I first put together a test version of the md5
stuff and asked this list to check it out on as many platforms as
possible I got one or two responses.  I now know the only way to get
more than a couple of responses is to threaten the code be committed. :(

Vince.
--
==========================================================================
Vince Vielhaber -- KA8CSH    email: vev@michvhf.com    http://www.pop4.net
         56K Nationwide Dialup from $16.00/mo at Pop4 Networking
        Online Campground Directory    http://www.camping-usa.com
       Online Giftshop Superstore    http://www.cloudninegifts.com
==========================================================================




Re: Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
> > That is from Vince's code, I think.  Can you suggest a fix?
> >
> > > What's your rush?  Is there a deadline now?
> >
> > I want to do SCM patch, then write presentation for LinuxWorld, and go
> > to Linuxworld. Then we are at the end of August.  Also, people need to
> > do the Java MD5 code, and if I want that before we start 7.2 beta, I
> > feel rushed.
>
> Interesting... When I first put together a test version of the md5
> stuff and asked this list to check it out on as many platforms as
> possible I got one or two responses.  I now know the only way to get
> more than a couple of responses is to threaten the code be committed. :(

Yep.  The cool part is that people are already using it (or at least
compiling it).

I am making good progress on the SCM credentials stuff now.  It will
work on FreeBSD and BSD/OS and hopefully others.  This, with clarified
pg_hba.conf, and the encrypted pg_shadow stuff should make our
authentication more secure in 7.2.  I never liked that TRUST option.

Vince, I can't figure out how that pgcrypto API for MD5.  I have to
strip out the general stuff, and when I do, do I leave MD5 stuff in
pgcrypto.  Confusing.

--
  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

Re: Re: Proposal for encrypting pg_shadow passwords

From
Vince Vielhaber
Date:
On Wed, 15 Aug 2001, Bruce Momjian wrote:

> > > That is from Vince's code, I think.  Can you suggest a fix?
> > >
> > > > What's your rush?  Is there a deadline now?
> > >
> > > I want to do SCM patch, then write presentation for LinuxWorld, and go
> > > to Linuxworld. Then we are at the end of August.  Also, people need to
> > > do the Java MD5 code, and if I want that before we start 7.2 beta, I
> > > feel rushed.
> >
> > Interesting... When I first put together a test version of the md5
> > stuff and asked this list to check it out on as many platforms as
> > possible I got one or two responses.  I now know the only way to get
> > more than a couple of responses is to threaten the code be committed. :(
>
> Yep.  The cool part is that people are already using it (or at least
> compiling it).
>
> I am making good progress on the SCM credentials stuff now.  It will
> work on FreeBSD and BSD/OS and hopefully others.  This, with clarified
> pg_hba.conf, and the encrypted pg_shadow stuff should make our
> authentication more secure in 7.2.  I never liked that TRUST option.
>
> Vince, I can't figure out how that pgcrypto API for MD5.  I have to
> strip out the general stuff, and when I do, do I leave MD5 stuff in
> pgcrypto.  Confusing.

I've never looked at pgcrypto, unfortunately it'll be a week or two
before I can even think about it (unless this weekend takes the ugly
turn it's trying so hard to take!).

Vince.
--
==========================================================================
Vince Vielhaber -- KA8CSH    email: vev@michvhf.com    http://www.pop4.net
         56K Nationwide Dialup from $16.00/mo at Pop4 Networking
        Online Campground Directory    http://www.camping-usa.com
       Online Giftshop Superstore    http://www.cloudninegifts.com
==========================================================================




Re: Re: Proposal for encrypting pg_shadow passwords

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
>> BTW, a protocol version bump for this is a horrid idea.  That will
>> create lots of compatibility problems for people, whether they use
>> the new auth mode or not.

> I thought protocol bumps were the way to handle such things.  The SCM
> patch does it as well.  How should I tell if I am talking to a >=7.2
> client?

You send it the new auth request code and see if it copes or not.
There's no need to bump the overall protocol version, and every reason
*not* to.

            regards, tom lane

Re: Re: Proposal for encrypting pg_shadow passwords

From
Bruce Momjian
Date:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> >> BTW, a protocol version bump for this is a horrid idea.  That will
> >> create lots of compatibility problems for people, whether they use
> >> the new auth mode or not.
>
> > I thought protocol bumps were the way to handle such things.  The SCM
> > patch does it as well.  How should I tell if I am talking to a >=7.2
> > client?
>
> You send it the new auth request code and see if it copes or not.
> There's no need to bump the overall protocol version, and every reason
> *not* to.

OK, patch attached.  Pretty nifty. Try MD5 first, and if it fails, try
crypt.  I tested by corrupting the MD5 on the client side, and the
crypt() fallback worked, but only if pg_shadow was plaintext.

--
  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: src/backend/libpq/auth.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/auth.c,v
retrieving revision 1.57
diff -c -r1.57 auth.c
*** src/backend/libpq/auth.c    2001/08/15 18:42:14    1.57
--- src/backend/libpq/auth.c    2001/08/16 04:22:50
***************
*** 501,513 ****
              status = recv_and_check_password_packet(port);
              break;

-         case uaCrypt:
-             sendAuthRequest(port, AUTH_REQ_CRYPT);
-             status = recv_and_check_password_packet(port);
-             break;
-
          case uaMD5:
              sendAuthRequest(port, AUTH_REQ_MD5);
              status = recv_and_check_password_packet(port);
              break;

--- 501,516 ----
              status = recv_and_check_password_packet(port);
              break;

          case uaMD5:
              sendAuthRequest(port, AUTH_REQ_MD5);
+             if ((status = recv_and_check_password_packet(port)) == STATUS_OK)
+                 break;
+             port->auth_method = uaCrypt;
+             /* Try crypt() for old client */
+             /* FALL THROUGH */
+
+         case uaCrypt:
+             sendAuthRequest(port, AUTH_REQ_CRYPT);
              status = recv_and_check_password_packet(port);
              break;

Index: src/backend/libpq/hba.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/hba.c,v
retrieving revision 1.62
diff -c -r1.62 hba.c
*** src/backend/libpq/hba.c    2001/08/15 18:42:15    1.62
--- src/backend/libpq/hba.c    2001/08/16 04:22:51
***************
*** 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);
--- 227,234 ----
          else if (strcmp(token, "reject") == 0)
              *userauth_p = uaReject;
          else if (strcmp(token, "crypt") == 0)
!             /* Try MD5 first; on failure, switch to crypt() */
!             *userauth_p = uaMD5;
          else
              *error_p = true;
          line = lnext(line);
Index: src/include/libpq/pqcomm.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/libpq/pqcomm.h,v
retrieving revision 1.56
diff -c -r1.56 pqcomm.h
*** src/include/libpq/pqcomm.h    2001/08/15 18:42:15    1.56
--- src/include/libpq/pqcomm.h    2001/08/16 04:23:00
***************
*** 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
--- 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
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.37
diff -c -r1.37 libpq-int.h
*** src/interfaces/libpq/libpq-int.h    2001/08/15 18:42:16    1.37
--- src/interfaces/libpq/libpq-int.h    2001/08/16 04:23:01
***************
*** 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.
--- 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.

Re: Re: Proposal for encrypting pg_shadow passwords

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> OK, patch attached.  Pretty nifty. Try MD5 first, and if it fails, try
> crypt.

What???

Where did *that* idea come from?  If I'm using the new auth method
because I don't think the old one is secure, I sure as heck don't want
an old (or deliberately-broken) client to cause a fallback to a less
secure method.

If MD5 is specified in the config file, and the client doesn't support
it, then you *fail*.  Full stop.

            regards, tom lane

Re: Re: Proposal for encrypting pg_shadow passwords

From
Marko Kreen
Date:
On Wed, Aug 15, 2001 at 08:09:15PM -0400, Bruce Momjian wrote:
> Vince, I can't figure out how that pgcrypto API for MD5.

In the 'new' code I sent today:

{
    PX_MD *md;
    uint8 *res;
    int err;

    err = px_find_digest("md5", &md);
    if (err) ...

    res = palloc(px_md_result_size(md));

    px_md_update(md, data, datalen);
    px_md_update(md, data2, data2len);

    px_md_finish(md, res);
    px_md_free(md);
}

--
marko