Re: Proposal for encrypting pg_shadow passwords - Mailing list pgsql-patches
From | Bruce Momjian |
---|---|
Subject | Re: Proposal for encrypting pg_shadow passwords |
Date | |
Msg-id | 200108150552.f7F5qZV16322@candle.pha.pa.us Whole thread Raw |
Responses |
Re: Re: Proposal for encrypting pg_shadow passwords
Re: Re: Proposal for encrypting pg_shadow passwords Re: Re: Proposal for encrypting pg_shadow passwords Re: Re: Proposal for encrypting pg_shadow passwords |
List | pgsql-patches |
Attached please find: the original proposal to encrypt pg_shadow a diff of the current CVS two new files (backend/libpq/md5.c and include/libpq/md5.h) which implement MD5 encryption (from Vince with cleanups) I have increased the protocol version from 2.0 -> 2.1. I use MD5 for all client encryption if the client supports it. I know we have portability problems with libc's crypt() this will fix that right away. Of course older clients and servers will still talk using libc's crypt(). It turns out that ODBC doesn't even do crypt authentication so I have not added MD5 code there yet. That will be another project because I have to add salt handling and stuff. Perhaps someone with more ODBC experience can do it. If they add crypt(), I can add MD5. I see code in connection.c: case AUTH_REQ_CRYPT: case AUTH_REQ_MD5: self->errormsg = "Password crypt authentication not supported"; self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED; return 0; I added enough so MD5 will fail just like crypt. Woo-hoo. JDBC needs to be done, but I am told MD5 is native to Java. I have made no changes to pg_dump because it will reload the password just as it was dumped, e.g. if they were encrypted, they will be loaded encrypted. If plaintext, they will load plaintext, unless they have changed postgresql.conf to true, in which case they will be encrypted on load if they weren't already encrypted. I didn't see any value to adding ENCRYPTED/UNENCRYPTED to pg_dump. Those flags are only useful for overriding postgresql.conf, but in this case the encryption has already happened. I think there is a problem with secondary password files. Because the client uses MD5 for encryption, it can't be used to check against crypt() based secondary passwords. If those passwords where MD5, it would be OK. The problem here is that the client doesn't check to see if it is going to use the secondary password or the pg_shadow password until _after_ it gets the password from the client. I could reorganize the code but I want to get people's opinion first. In fact, if we have portability problems with libc crypt(), should we be using crypt()-based secondary passwords at all? I know there was a report just this week about that. Perhaps it would be better to remove the entire secondary password mechanism and allow a list of valid username to appear in pg_hba.conf in the AUTH_ARGUMENT column. I think listing usernames that can connect to the database is the most valuable thing about secondary password files anyway. I know you can copy /etc/shadow and use that but the crypt() problem seems insurmountable and I know others aren't excited about that feature. Oh, well, that's about it. Seems to work pretty well. > Here is my proposal to fix the security problem of storing cleartext > passwords in pg_shadow. The solution is somewhat complex because we > have to allow 7.2 servers to communicate with 7.1 clients, at least for > a short while. > > Here is a summary of what we currently do and proposed solutions. > > > PG_HBA.CONF > ----------- > pg_hba.conf has three authentication options of interest to this > discussion: > > trust: no authentication required > > password: plaintext password is sent over network from client > to server > > crypt: random salt is sent to client; client encrypts using that salt > and returns encrypted password to server. Server encrypts pg_shadow > password with same random salt and compares. This is why current > pg_shadow password is cleartext. (Call this "crypt authentication".) > > > DOUBLE ENCRYPTION > ----------------- > The solution for encrypting pg_shadow passwords is to encrypt using a > salt when stored in pg_shadow, and to generate a random salt for each > authentication request. Send _both_ salts to the client, let the client > double encrypt using the pg_shadow salt first, then the random salt, and > send it back. The server encrypt using only the random salt and > compares. > > As soon as we encrypt pg_shadow passwords, we can't communicate with > pre-7.2 clients using crypt-authentication. Actually, we could, but we > would have to send the same pg_shadow salt every time, which is insecure > because someone snooping the wire could just play back the same reply so > it is better to just fail such authentications. > > > USER INTERFACE > -------------- > So, my idea is to add an option to CREATE/ALTER USER: > > CREATE USER WITH ENCRYPTED PASSWORD 'fred'; > CREATE USER WITH UNENCRYPTED PASSWORD 'fred'; > ALTER USER WITH ENCRYPTED PASSWORD 'fred'; > ALTER USER WITH UNENCRYPTED PASSWORD 'fred'; > > Keep in mind ENCRYPTED/UNENCRYPTED controls how it is stored in > pg_shadow, not wither "fred" is a cleartext or preencrypted password. > We plan to prefix md5 passwords with "md5" to handle this issue. (Md5 > passwords are also 35-characters in length.) > > Also add a new GUC config option: > > SET password_encrypted_default TO 'OFF'; > > It would ship as OFF in 7.2 and can be removed in a later release. Once > all clients are upgraded to 7.2, you can change the default to ON and do > ALTER USER WITH PASSWORD 'fred' to encrypt the pg_shadow passwords. The > passwords are in cleartext in pg_shadow so it is easy to do. > > > MD5 > --- > I assume we will use MD5 for encryption of pg_shadow passwords. The > letters "md5" will appear at the start of the password string and it > will be exactly 35 characters. Vince sent me the code. We will need to > add MD5 capability to libpq, ODBC, and JDBC. (I hope JDBC will not be a > problem.) When using CREATE/ALTER user, the system will automatically > consider a 35-character string that starts with "md5" to be a > pre-md5-encrypted password, while anything else will be md5 encrypted. > > > SECONDARY PASSWORD FILES > ------------------------ > To add complexity to this, we also support secondary password files. > (See pg_hba.conf.sample and pg_password manual in CVS for updated > descriptions.) These password files allow encrypted passwords in the > same format as they appear in traditional /etc/passwd. (Call this > crypt-style passwords.) I realize most BSD's use MD5 in /etc/shadow > now. > > Right now we can use passwords from the file only if we use > password-authentication. We can't use crypt-authentication because the > passwords already have a salt and we don't want to sent the same salt > every time. One nice feature of secondary passwords is you can copy > /etc/passwd or /etc/shadow and use that as your secondary password file > for PostgreSQL. I don't know how many people use that but it is nice > feature. Remember the secondary password files sit in /data which is > readable only by the PostgreSQL install user. > > > DOUBLE-CRYPT ENCRYPTION > ----------------------- > So, we are going to add a new double-MD5 encryption protocol to allow > pg_shadow passwords to be encrypted. Do we also add a > double-crypt-style-password protocol to allow crypt-authentication with > secondary password files that use crypt-style passwords or just require > the secondary password files to use MD5? > > > Comments? > > > -- > Bruce Momjian | http://candle.pha.pa.us > pgman@candle.pha.pa.us | (610) 853-3000 > + If your life is a hard drive, | 830 Blythe Avenue > + Christ can be your backup. | Drexel Hill, Pennsylvania 19026 -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 853-3000 + If your life is a hard drive, | 830 Blythe Avenue + Christ can be your backup. | Drexel Hill, Pennsylvania 19026 Index: doc/src/sgml/client-auth.sgml =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v retrieving revision 1.15 diff -c -r1.15 client-auth.sgml *** doc/src/sgml/client-auth.sgml 2001/08/01 23:25:39 1.15 --- doc/src/sgml/client-auth.sgml 2001/08/15 05:12:48 *************** *** 205,215 **** <para> Like the <literal>password</literal> method, but the password is sent over the wire encrypted using a simple ! challenge-response protocol. This is still not ! cryptographically secure but it protects against incidental wire-sniffing. The name of a file may follow the ! <literal>crypt</literal> keyword that contains a list of users ! that this record pertains to. </para> </listitem> </varlistentry> --- 205,214 ---- <para> Like the <literal>password</literal> method, but the password is sent over the wire encrypted using a simple ! challenge-response protocol. This protects against incidental wire-sniffing. The name of a file may follow the ! <literal>crypt</literal> keyword. It contains a list of users ! for this record. </para> </listitem> </varlistentry> Index: doc/src/sgml/protocol.sgml =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/protocol.sgml,v retrieving revision 1.18 diff -c -r1.18 protocol.sgml *** doc/src/sgml/protocol.sgml 2001/06/22 23:27:48 1.18 --- doc/src/sgml/protocol.sgml 2001/08/15 05:12:49 *************** *** 1295,1301 **** </Term> <ListItem> <Para> ! The encrypted (using crypt()) password. </Para> </ListItem> </VarListEntry> --- 1295,1301 ---- </Term> <ListItem> <Para> ! The encrypted (using MD5 or crypt()) password. </Para> </ListItem> </VarListEntry> Index: doc/src/sgml/runtime.sgml =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/runtime.sgml,v retrieving revision 1.74 diff -c -r1.74 runtime.sgml *** doc/src/sgml/runtime.sgml 2001/08/09 16:20:43 1.74 --- doc/src/sgml/runtime.sgml 2001/08/15 05:12:52 *************** *** 968,973 **** --- 968,985 ---- <para> <variablelist> <varlistentry> + <term>AUSTRALIAN_TIMEZONES (<type>bool</type>)</term> + <listitem> + <para> + If set to true, <literal>CST</literal>, <literal>EST</literal>, + and <literal>SAT</literal> are interpreted as Australian + timezones rather than as North American Central/Eastern + Timezones and Saturday. The default is false. + </para> + </listitem> + </varlistentry> + + <varlistentry> <indexterm> <primary>deadlock</primary> <secondary>timeout</secondary> *************** *** 1256,1273 **** keyword to exclude subtables. See the SQL language reference and the <citetitle>User's Guide</citetitle> for more information about inheritance. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>AUSTRALIAN_TIMEZONES (<type>bool</type>)</term> - <listitem> - <para> - If set to true, <literal>CST</literal>, <literal>EST</literal>, - and <literal>SAT</literal> are interpreted as Australian - timezones rather than as North American Central/Eastern - Timezones and Saturday. The default is false. </para> </listitem> </varlistentry> --- 1268,1273 ---- Index: doc/src/sgml/ref/alter_user.sgml =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/ref/alter_user.sgml,v retrieving revision 1.14 diff -c -r1.14 alter_user.sgml *** doc/src/sgml/ref/alter_user.sgml 2001/07/10 22:09:27 1.14 --- doc/src/sgml/ref/alter_user.sgml 2001/08/15 05:12:52 *************** *** 27,33 **** where <replaceable class="PARAMETER">option</replaceable> can be: ! PASSWORD '<replaceable class="PARAMETER">password</replaceable>' | CREATEDB | NOCREATEDB | CREATEUSER | NOCREATEUSER | VALID UNTIL '<replaceable class="PARAMETER">abstime</replaceable>' --- 27,33 ---- where <replaceable class="PARAMETER">option</replaceable> can be: ! [ ENCRYPTED | UNENCRYPTED ] PASSWORD '<replaceable class="PARAMETER">password</replaceable>' | CREATEDB | NOCREATEDB | CREATEUSER | NOCREATEUSER | VALID UNTIL '<replaceable class="PARAMETER">abstime</replaceable>' *************** *** 53,62 **** </varlistentry> <varlistentry> ! <term><replaceable class="PARAMETER">password</replaceable></term> <listitem> <para> The new password to be used for this account. </para> </listitem> </varlistentry> --- 53,65 ---- </varlistentry> <varlistentry> ! <term><replaceable class="PARAMETER">[ encrypted | unencrypted ] password</replaceable></term> <listitem> <para> The new password to be used for this account. + <literal>Encrypted</literal>/ <literal>unencrypted</literal> + controls whether the password is stored encrypted in the + database. </para> </listitem> </varlistentry> Index: doc/src/sgml/ref/create_user.sgml =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/ref/create_user.sgml,v retrieving revision 1.17 diff -c -r1.17 create_user.sgml *** doc/src/sgml/ref/create_user.sgml 2001/07/10 22:09:27 1.17 --- doc/src/sgml/ref/create_user.sgml 2001/08/15 05:12:52 *************** *** 28,34 **** where <replaceable class="PARAMETER">option</replaceable> can be: SYSID <replaceable class="PARAMETER">uid</replaceable> ! | PASSWORD '<replaceable class="PARAMETER">password</replaceable>' | CREATEDB | NOCREATEDB | CREATEUSER | NOCREATEUSER | IN GROUP <replaceable class="PARAMETER">groupname</replaceable> [, ...] --- 28,34 ---- where <replaceable class="PARAMETER">option</replaceable> can be: SYSID <replaceable class="PARAMETER">uid</replaceable> ! | [ ENCRYPTED | UNENCRYPTED ] PASSWORD '<replaceable class="PARAMETER">password</replaceable>' | CREATEDB | NOCREATEDB | CREATEUSER | NOCREATEUSER | IN GROUP <replaceable class="PARAMETER">groupname</replaceable> [, ...] *************** *** 72,83 **** </varlistentry> <varlistentry> ! <term><replaceable class="parameter">password</replaceable></term> <listitem> <para> Sets the user's password. If you do not plan to use password authentication you can omit this option, otherwise the user won't be able to connect to a password-authenticated server. See the chapter on client authentication in the <citetitle>Administrator's Guide</citetitle> for details on how to set up authentication mechanisms. --- 72,90 ---- </varlistentry> <varlistentry> ! <term><replaceable class="parameter">[ encrypted | unencrypted ] password</replaceable></term> <listitem> <para> Sets the user's password. If you do not plan to use password authentication you can omit this option, otherwise the user won't be able to connect to a password-authenticated server. + </para> + <para> + <literal>ENCRYPTED/UNENCRYPTED</literal> controls whether the + password is stored encrypted in the database. Older clients may + have trouble communicating using encrypted password storage. + </para> + <para> See the chapter on client authentication in the <citetitle>Administrator's Guide</citetitle> for details on how to set up authentication mechanisms. Index: src/backend/commands/user.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/commands/user.c,v retrieving revision 1.79 diff -c -r1.79 user.c *** src/backend/commands/user.c 2001/07/12 18:02:59 1.79 --- src/backend/commands/user.c 2001/08/15 05:12:53 *************** *** 25,30 **** --- 25,31 ---- #include "catalog/indexing.h" #include "commands/user.h" #include "libpq/crypt.h" + #include "libpq/md5.h" #include "miscadmin.h" #include "utils/array.h" #include "utils/builtins.h" *************** *** 34,41 **** static void CheckPgUserAclNotNull(void); - /*--------------------------------------------------------------------- * write_password_file / update_pg_pwd * --- 35,42 ---- static void CheckPgUserAclNotNull(void); + extern bool Password_encryption; /*--------------------------------------------------------------------- * write_password_file / update_pg_pwd * *************** *** 201,272 **** int max_id = -1; List *item, *option; char *password = NULL; /* PostgreSQL user password */ int sysid = 0; /* PgSQL system id (valid if havesysid) */ bool createdb = false; /* Can the user create databases? */ bool createuser = false; /* Can this user create users? */ List *groupElts = NIL; /* The groups the user is a member of */ char *validUntil = NULL; /* The time the login is valid until */ ! DefElem *dpassword = NULL; ! DefElem *dsysid = NULL; ! DefElem *dcreatedb = NULL; ! DefElem *dcreateuser = NULL; ! DefElem *dgroupElts = NULL; ! DefElem *dvalidUntil = NULL; /* Extract options from the statement node tree */ foreach(option, stmt->options) { DefElem *defel = (DefElem *) lfirst(option); ! if (strcasecmp(defel->defname, "password") == 0) { ! if (dpassword) ! elog(ERROR, "CREATE USER: conflicting options"); ! dpassword = defel; ! } ! else if (strcasecmp(defel->defname, "sysid") == 0) { ! if (dsysid) ! elog(ERROR, "CREATE USER: conflicting options"); ! dsysid = defel; ! } ! else if (strcasecmp(defel->defname, "createdb") == 0) { ! if (dcreatedb) ! elog(ERROR, "CREATE USER: conflicting options"); ! dcreatedb = defel; ! } ! else if (strcasecmp(defel->defname, "createuser") == 0) { ! if (dcreateuser) ! elog(ERROR, "CREATE USER: conflicting options"); ! dcreateuser = defel; ! } ! else if (strcasecmp(defel->defname, "groupElts") == 0) { ! if (dgroupElts) ! elog(ERROR, "CREATE USER: conflicting options"); ! dgroupElts = defel; ! } ! else if (strcasecmp(defel->defname, "validUntil") == 0) { ! if (dvalidUntil) ! elog(ERROR, "CREATE USER: conflicting options"); ! dvalidUntil = defel; ! } ! else ! elog(ERROR,"CREATE USER: option \"%s\" not recognized", ! defel->defname); ! } ! if (dcreatedb) createdb = intVal(dcreatedb->arg) != 0; ! if (dcreateuser) createuser = intVal(dcreateuser->arg) != 0; ! if (dsysid) { sysid = intVal(dsysid->arg); havesysid = true; } ! if (dvalidUntil) validUntil = strVal(dvalidUntil->arg); ! if (dpassword) password = strVal(dpassword->arg); ! if (dgroupElts) groupElts = (List *) dgroupElts->arg; /* Check some permissions first */ --- 202,281 ---- int max_id = -1; List *item, *option; char *password = NULL; /* PostgreSQL user password */ + bool encrypt_password = Password_encryption; /* encrypt password? */ + char encrypted_password[MD5_PASSWD_LEN+1]; int sysid = 0; /* PgSQL system id (valid if havesysid) */ bool createdb = false; /* Can the user create databases? */ bool createuser = false; /* Can this user create users? */ List *groupElts = NIL; /* The groups the user is a member of */ char *validUntil = NULL; /* The time the login is valid until */ ! DefElem *dpassword = NULL; ! DefElem *dsysid = NULL; ! DefElem *dcreatedb = NULL; ! DefElem *dcreateuser = NULL; ! DefElem *dgroupElts = NULL; ! DefElem *dvalidUntil = NULL; /* Extract options from the statement node tree */ foreach(option, stmt->options) { DefElem *defel = (DefElem *) lfirst(option); ! if (strcasecmp(defel->defname, "password") == 0 || ! strcasecmp(defel->defname, "encryptedPassword") == 0 || ! strcasecmp(defel->defname, "unencryptedPassword") == 0) { ! if (dpassword) ! elog(ERROR, "CREATE USER: conflicting options"); ! dpassword = defel; ! if (strcasecmp(defel->defname, "encryptedPassword") == 0) ! encrypt_password = true; ! else if (strcasecmp(defel->defname, "unencryptedPassword") == 0) ! encrypt_password = false; ! } ! else if (strcasecmp(defel->defname, "sysid") == 0) { ! if (dsysid) ! elog(ERROR, "CREATE USER: conflicting options"); ! dsysid = defel; ! } ! else if (strcasecmp(defel->defname, "createdb") == 0) { ! if (dcreatedb) ! elog(ERROR, "CREATE USER: conflicting options"); ! dcreatedb = defel; ! } ! else if (strcasecmp(defel->defname, "createuser") == 0) { ! if (dcreateuser) ! elog(ERROR, "CREATE USER: conflicting options"); ! dcreateuser = defel; ! } ! else if (strcasecmp(defel->defname, "groupElts") == 0) { ! if (dgroupElts) ! elog(ERROR, "CREATE USER: conflicting options"); ! dgroupElts = defel; ! } ! else if (strcasecmp(defel->defname, "validUntil") == 0) { ! if (dvalidUntil) ! elog(ERROR, "CREATE USER: conflicting options"); ! dvalidUntil = defel; ! } ! else ! elog(ERROR,"CREATE USER: option \"%s\" not recognized", ! defel->defname); ! } ! if (dcreatedb) createdb = intVal(dcreatedb->arg) != 0; ! if (dcreateuser) createuser = intVal(dcreateuser->arg) != 0; ! if (dsysid) { sysid = intVal(dsysid->arg); havesysid = true; } ! if (dvalidUntil) validUntil = strVal(dvalidUntil->arg); ! if (dpassword) password = strVal(dpassword->arg); ! if (dgroupElts) groupElts = (List *) dgroupElts->arg; /* Check some permissions first */ *************** *** 337,344 **** new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser); if (password) ! new_record[Anum_pg_shadow_passwd - 1] = ! DirectFunctionCall1(textin, CStringGetDatum(password)); if (validUntil) new_record[Anum_pg_shadow_valuntil - 1] = DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil)); --- 346,363 ---- new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser); if (password) ! { ! if (!encrypt_password || isMD5(password)) ! new_record[Anum_pg_shadow_passwd - 1] = ! DirectFunctionCall1(textin, CStringGetDatum(password)); ! else ! { ! if (!EncryptMD5(password, stmt->user, encrypted_password)) ! elog(ERROR, "CREATE USER: password encryption failed"); ! new_record[Anum_pg_shadow_passwd - 1] = ! DirectFunctionCall1(textin, CStringGetDatum(encrypted_password)); ! } ! } if (validUntil) new_record[Anum_pg_shadow_valuntil - 1] = DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil)); *************** *** 418,423 **** --- 437,444 ---- bool null; List *option; char *password = NULL; /* PostgreSQL user password */ + bool encrypt_password = Password_encryption; /* encrypt password? */ + char encrypted_password[MD5_PASSWD_LEN+1]; int createdb = -1; /* Can the user create databases? */ int createuser = -1; /* Can this user create users? */ char *validUntil = NULL; /* The time the login is valid until */ *************** *** 431,440 **** { DefElem *defel = (DefElem *) lfirst(option); ! if (strcasecmp(defel->defname, "password") == 0) { if (dpassword) elog(ERROR, "ALTER USER: conflicting options"); dpassword = defel; } else if (strcasecmp(defel->defname, "createdb") == 0) { if (dcreatedb) --- 452,467 ---- { DefElem *defel = (DefElem *) lfirst(option); ! if (strcasecmp(defel->defname, "password") == 0 || ! strcasecmp(defel->defname, "encryptedPassword") == 0 || ! strcasecmp(defel->defname, "unencryptedPassword") == 0) { if (dpassword) elog(ERROR, "ALTER USER: conflicting options"); dpassword = defel; + if (strcasecmp(defel->defname, "encryptedPassword") == 0) + encrypt_password = true; + else if (strcasecmp(defel->defname, "unencryptedPassword") == 0) + encrypt_password = false; } else if (strcasecmp(defel->defname, "createdb") == 0) { if (dcreatedb) *************** *** 445,461 **** if (dcreateuser) elog(ERROR, "ALTER USER: conflicting options"); dcreateuser = defel; ! } else if (strcasecmp(defel->defname, "validUntil") == 0) { if (dvalidUntil) elog(ERROR, "ALTER USER: conflicting options"); dvalidUntil = defel; } ! else elog(ERROR,"ALTER USER: option \"%s\" not recognized", defel->defname); } ! if (dcreatedb) createdb = intVal(dcreatedb->arg); if (dcreateuser) --- 472,488 ---- if (dcreateuser) elog(ERROR, "ALTER USER: conflicting options"); dcreateuser = defel; ! } else if (strcasecmp(defel->defname, "validUntil") == 0) { if (dvalidUntil) elog(ERROR, "ALTER USER: conflicting options"); dvalidUntil = defel; } ! else elog(ERROR,"ALTER USER: option \"%s\" not recognized", defel->defname); } ! if (dcreatedb) createdb = intVal(dcreatedb->arg); if (dcreateuser) *************** *** 464,471 **** validUntil = strVal(dvalidUntil->arg); if (dpassword) password = strVal(dpassword->arg); ! ! if (password) CheckPgUserAclNotNull(); /* must be superuser or just want to change your own password */ --- 491,498 ---- validUntil = strVal(dvalidUntil->arg); if (dpassword) password = strVal(dpassword->arg); ! ! if (password) CheckPgUserAclNotNull(); /* must be superuser or just want to change your own password */ *************** *** 552,559 **** /* password */ if (password) { ! new_record[Anum_pg_shadow_passwd - 1] = ! DirectFunctionCall1(textin, CStringGetDatum(password)); new_record_nulls[Anum_pg_shadow_passwd - 1] = ' '; } else --- 579,594 ---- /* password */ if (password) { ! if (!encrypt_password || isMD5(password)) ! new_record[Anum_pg_shadow_passwd - 1] = ! DirectFunctionCall1(textin, CStringGetDatum(password)); ! else ! { ! if (!EncryptMD5(password, stmt->user, encrypted_password)) ! elog(ERROR, "CREATE USER: password encryption failed"); ! new_record[Anum_pg_shadow_passwd - 1] = ! DirectFunctionCall1(textin, CStringGetDatum(encrypted_password)); ! } new_record_nulls[Anum_pg_shadow_passwd - 1] = ' '; } else *************** *** 793,832 **** Datum new_record[Natts_pg_group]; char new_record_nulls[Natts_pg_group]; List *item, ! *option, *newlist = NIL; ArrayType *userarray; ! int sysid = 0; ! List *userElts = NIL; ! DefElem *dsysid = NULL; ! DefElem *duserElts = NULL; foreach(option, stmt->options) { DefElem *defel = (DefElem *) lfirst(option); ! if (strcasecmp(defel->defname, "sysid") == 0) { ! if (dsysid) ! elog(ERROR, "CREATE GROUP: conflicting options"); ! dsysid = defel; ! } ! else if (strcasecmp(defel->defname, "userElts") == 0) { ! if (duserElts) ! elog(ERROR, "CREATE GROUP: conflicting options"); ! duserElts = defel; ! } ! else ! elog(ERROR,"CREATE GROUP: option \"%s\" not recognized", ! defel->defname); ! } ! if (dsysid) { sysid = intVal(dsysid->arg); havesysid = true; } ! if (duserElts) userElts = (List *) duserElts->arg; /* --- 828,867 ---- Datum new_record[Natts_pg_group]; char new_record_nulls[Natts_pg_group]; List *item, ! *option, *newlist = NIL; ArrayType *userarray; ! int sysid = 0; ! List *userElts = NIL; ! DefElem *dsysid = NULL; ! DefElem *duserElts = NULL; foreach(option, stmt->options) { DefElem *defel = (DefElem *) lfirst(option); ! if (strcasecmp(defel->defname, "sysid") == 0) { ! if (dsysid) ! elog(ERROR, "CREATE GROUP: conflicting options"); ! dsysid = defel; ! } ! else if (strcasecmp(defel->defname, "userElts") == 0) { ! if (duserElts) ! elog(ERROR, "CREATE GROUP: conflicting options"); ! duserElts = defel; ! } ! else ! elog(ERROR,"CREATE GROUP: option \"%s\" not recognized", ! defel->defname); ! } ! if (dsysid) { sysid = intVal(dsysid->arg); havesysid = true; } ! if (duserElts) userElts = (List *) duserElts->arg; /* Index: src/backend/libpq/Makefile =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/Makefile,v retrieving revision 1.24 diff -c -r1.24 Makefile *** src/backend/libpq/Makefile 2000/08/25 10:00:30 1.24 --- src/backend/libpq/Makefile 2001/08/15 05:12:53 *************** *** 15,21 **** # be-fsstubs is here for historical reasons, probably belongs elsewhere OBJS = be-fsstubs.o \ ! auth.o crypt.o hba.o password.o \ pqcomm.o pqformat.o pqpacket.o pqsignal.o util.o --- 15,21 ---- # be-fsstubs is here for historical reasons, probably belongs elsewhere OBJS = be-fsstubs.o \ ! auth.o crypt.o hba.o md5.o password.o \ pqcomm.o pqformat.o pqpacket.o pqsignal.o util.o Index: src/backend/libpq/auth.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/auth.c,v retrieving revision 1.56 diff -c -r1.56 auth.c *** src/backend/libpq/auth.c 2001/08/07 10:44:13 1.56 --- src/backend/libpq/auth.c 2001/08/15 05:12:53 *************** *** 420,428 **** authmethod = "IDENT"; break; case uaPassword: - authmethod = "Password"; - break; case uaCrypt: authmethod = "Password"; break; } --- 420,427 ---- authmethod = "IDENT"; break; case uaPassword: case uaCrypt: + case uaMD5: authmethod = "Password"; break; } *************** *** 507,512 **** --- 506,516 ---- status = recv_and_check_password_packet(port); break; + case uaMD5: + sendAuthRequest(port, AUTH_REQ_MD5); + status = recv_and_check_password_packet(port); + break; + case uaTrust: status = STATUS_OK; break; *************** *** 532,538 **** pq_sendint(&buf, (int32) areq, sizeof(int32)); /* Add the salt for encrypted passwords. */ ! if (areq == AUTH_REQ_CRYPT) { pq_sendint(&buf, port->salt[0], 1); pq_sendint(&buf, port->salt[1], 1); --- 536,542 ---- pq_sendint(&buf, (int32) areq, sizeof(int32)); /* Add the salt for encrypted passwords. */ ! if (areq == AUTH_REQ_CRYPT || areq == AUTH_REQ_MD5) { pq_sendint(&buf, port->salt[0], 1); pq_sendint(&buf, port->salt[1], 1); *************** *** 557,563 **** if (pq_eof() == EOF || pq_getint(&len, 4) == EOF) return STATUS_ERROR; /* client didn't want to send password */ initStringInfo(&buf); ! pq_getstr(&buf); if (DebugLvl) fprintf(stderr, "received password packet with len=%d, pw=%s\n", --- 561,567 ---- if (pq_eof() == EOF || pq_getint(&len, 4) == EOF) return STATUS_ERROR; /* client didn't want to send password */ initStringInfo(&buf); ! pq_getstr(&buf); /* receive password */ if (DebugLvl) fprintf(stderr, "received password packet with len=%d, pw=%s\n", *************** *** 579,585 **** if (port->auth_arg[0] != '\0') return verify_password(port, user, password); ! return crypt_verify(port, user, password); } --- 583,589 ---- if (port->auth_arg[0] != '\0') return verify_password(port, user, password); ! return md5_crypt_verify(port, user, password); } *************** *** 594,600 **** MsgType msgtype = (MsgType) port->proto; /* Handle the authentication that's offered. */ - switch (msgtype) { case STARTUP_KRB4_MSG: --- 598,603 ---- *************** *** 634,639 **** --- 637,643 ---- switch (port->auth_method) { case uaCrypt: + case uaMD5: case uaReject: status = STATUS_ERROR; break; Index: src/backend/libpq/crypt.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/crypt.c,v retrieving revision 1.32 diff -c -r1.32 crypt.c *** src/backend/libpq/crypt.c 2001/06/23 23:26:17 1.32 --- src/backend/libpq/crypt.c 2001/08/15 05:12:54 *************** *** 19,24 **** --- 19,25 ---- #include "postgres.h" #include "libpq/crypt.h" + #include "libpq/md5.h" #include "miscadmin.h" #include "storage/fd.h" #include "utils/nabstime.h" *************** *** 254,260 **** /*-------------------------------------------------------------------------*/ int ! crypt_verify(const Port *port, const char *user, const char *pgpass) { char *passwd, --- 255,261 ---- /*-------------------------------------------------------------------------*/ int ! md5_crypt_verify(const Port *port, const char *user, const char *pgpass) { char *passwd, *************** *** 280,288 **** * Compare with the encrypted or plain password depending on the * authentication method being used for this connection. */ ! ! crypt_pwd = ! (port->auth_method == uaCrypt ? crypt(passwd, port->salt) : passwd); if (!strcmp(pgpass, crypt_pwd)) { --- 281,327 ---- * Compare with the encrypted or plain password depending on the * authentication method being used for this connection. */ ! switch (port->auth_method) ! { ! case uaCrypt: ! crypt_pwd = crypt(passwd, port->salt); ! break; ! case uaMD5: ! crypt_pwd = palloc(MD5_PASSWD_LEN+1); ! ! if (isMD5(passwd)) ! { ! if (!EncryptMD5(passwd + strlen("md5"), ! (char *)port->salt, crypt_pwd)) ! { ! pfree(crypt_pwd); ! return STATUS_ERROR; ! } ! } ! else ! { ! char *crypt_pwd2 = palloc(MD5_PASSWD_LEN+1); ! ! if (!EncryptMD5(passwd, port->user, crypt_pwd2)) ! { ! pfree(crypt_pwd); ! pfree(crypt_pwd2); ! return STATUS_ERROR; ! } ! if (!EncryptMD5(crypt_pwd2 + strlen("md5"), port->salt, ! crypt_pwd)) ! { ! pfree(crypt_pwd); ! pfree(crypt_pwd2); ! return STATUS_ERROR; ! } ! pfree(crypt_pwd2); ! } ! break; ! default: ! crypt_pwd = passwd; ! break; ! } if (!strcmp(pgpass, crypt_pwd)) { *************** *** 302,310 **** retval = STATUS_OK; } ! pfree((void *) passwd); if (valuntil) ! pfree((void *) valuntil); return retval; } --- 341,351 ---- retval = STATUS_OK; } ! pfree(passwd); if (valuntil) ! pfree(valuntil); ! if (port->auth_method == uaMD5) ! pfree(crypt_pwd); return retval; } Index: src/backend/libpq/hba.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/hba.c,v retrieving revision 1.61 diff -c -r1.61 hba.c *** src/backend/libpq/hba.c 2001/08/02 14:39:35 1.61 --- src/backend/libpq/hba.c 2001/08/15 05:12:54 *************** *** 203,210 **** * *error_p. line points to the next token of the line. */ static void ! parse_hba_auth(List *line, UserAuth *userauth_p, char *auth_arg, ! bool *error_p) { char *token; --- 203,210 ---- * *error_p. line points to the next token of the line. */ static void ! parse_hba_auth(List *line, ProtocolVersion proto, UserAuth *userauth_p, ! char *auth_arg, bool *error_p) { char *token; *************** *** 227,233 **** else if (strcmp(token, "reject") == 0) *userauth_p = uaReject; else if (strcmp(token, "crypt") == 0) ! *userauth_p = uaCrypt; else *error_p = true; line = lnext(line); --- 227,241 ---- else if (strcmp(token, "reject") == 0) *userauth_p = uaReject; else if (strcmp(token, "crypt") == 0) ! { ! /* if the client supports it, use MD5 */ ! if (PG_PROTOCOL_MAJOR(proto) > 2 || ! (PG_PROTOCOL_MAJOR(proto) == 2 && ! PG_PROTOCOL_MINOR(proto) >= 1)) ! *userauth_p = uaMD5; ! else ! *userauth_p = uaCrypt; ! } else *error_p = true; line = lnext(line); *************** *** 285,291 **** line = lnext(line); if (!line) goto hba_syntax; ! parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p); if (*error_p) goto hba_syntax; --- 293,300 ---- line = lnext(line); if (!line) goto hba_syntax; ! parse_hba_auth(line, port->proto, &port->auth_method, ! port->auth_arg, error_p); if (*error_p) goto hba_syntax; *************** *** 354,360 **** line = lnext(line); if (!line) goto hba_syntax; ! parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p); if (*error_p) goto hba_syntax; --- 363,370 ---- line = lnext(line); if (!line) goto hba_syntax; ! parse_hba_auth(line, port->proto, &port->auth_method, ! port->auth_arg, error_p); if (*error_p) goto hba_syntax; Index: src/backend/libpq/password.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/password.c,v retrieving revision 1.37 diff -c -r1.37 password.c *** src/backend/libpq/password.c 2001/06/18 16:11:30 1.37 --- src/backend/libpq/password.c 2001/08/15 05:12:54 *************** *** 82,92 **** * the current code needs non-encrypted passwords to * encrypt with a random salt. */ ! if (port->auth_method == uaCrypt ! || test_pw == NULL || test_pw[0] == '\0' ! || strcmp(test_pw, "+") == 0) ! return crypt_verify(port, user, password); if (strcmp(crypt(password, test_pw), test_pw) == 0) { /* it matched. */ --- 82,95 ---- * the current code needs non-encrypted passwords to * encrypt with a random salt. */ ! if (port->auth_method == uaCrypt || ! port->auth_method == uaMD5 || ! test_pw == NULL || ! test_pw[0] == '\0' || ! strcmp(test_pw, "+") == 0) ! return md5_crypt_verify(port, user, password); + /* external password file is crypt-only */ if (strcmp(crypt(password, test_pw), test_pw) == 0) { /* it matched. */ Index: src/backend/libpq/pg_hba.conf.sample =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/pg_hba.conf.sample,v retrieving revision 1.23 diff -c -r1.23 pg_hba.conf.sample *** src/backend/libpq/pg_hba.conf.sample 2001/08/01 23:25:39 1.23 --- src/backend/libpq/pg_hba.conf.sample 2001/08/15 05:12:55 *************** *** 3,9 **** # # # This file controls: - # # o which hosts are allowed to connect # o how users are authenticated on each host # o databases accessible by each host --- 3,8 ---- *************** *** 24,30 **** # ============ # # There are three types of records: - # # o host # o hostssl # o local --- 23,28 ---- *************** *** 40,46 **** # host DBNAME IP_ADDRESS ADDRESS_MASK AUTH_TYPE [AUTH_ARGUMENT] # # DBNAME can be: - # # o the name of a PostgreSQL database # o "all" to indicate all databases # o "sameuser" to allow access only to databases with the same --- 38,43 ---- *************** *** 80,86 **** # allowed only if this record type appears. # # Format: - # # local DBNAME AUTH_TYPE [AUTH_ARGUMENT] # # This format is identical to the "host" record type except the IP_ADDRESS --- 77,82 ---- *************** *** 219,222 **** local all trust host all 127.0.0.1 255.255.255.255 trust - --- 215,217 ---- Index: src/backend/parser/gram.y =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/parser/gram.y,v retrieving revision 2.244 diff -c -r2.244 gram.y *** src/backend/parser/gram.y 2001/08/13 21:34:51 2.244 --- src/backend/parser/gram.y 2001/08/15 05:13:01 *************** *** 306,312 **** CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR, DAY_P, DEC, DECIMAL, DECLARE, DEFAULT, DELETE, DESC, DISTINCT, DOUBLE, DROP, ! ELSE, END_TRANS, ESCAPE, EXCEPT, EXECUTE, EXISTS, EXTRACT, FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL, GLOBAL, GRANT, GROUP, HAVING, HOUR_P, IN, INNER_P, INSENSITIVE, INSERT, INTERSECT, INTERVAL, INTO, IS, --- 306,312 ---- CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR, DAY_P, DEC, DECIMAL, DECLARE, DEFAULT, DELETE, DESC, DISTINCT, DOUBLE, DROP, ! ELSE, ENCRYPTED, END_TRANS, ESCAPE, EXCEPT, EXECUTE, EXISTS, EXTRACT, FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL, GLOBAL, GRANT, GROUP, HAVING, HOUR_P, IN, INNER_P, INSENSITIVE, INSERT, INTERSECT, INTERVAL, INTO, IS, *************** *** 319,325 **** SCHEMA, SCROLL, SECOND_P, SELECT, SESSION, SESSION_USER, SET, SOME, SUBSTRING, TABLE, TEMPORARY, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE, TO, TRAILING, TRANSACTION, TRIM, TRUE_P, ! UNION, UNIQUE, UNKNOWN, UPDATE, USER, USING, VALUES, VARCHAR, VARYING, VIEW, WHEN, WHERE, WITH, WORK, YEAR_P, ZONE --- 319,325 ---- SCHEMA, SCROLL, SECOND_P, SELECT, SESSION, SESSION_USER, SET, SOME, SUBSTRING, TABLE, TEMPORARY, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE, TO, TRAILING, TRANSACTION, TRIM, TRUE_P, ! UNENCRYPTED, UNION, UNIQUE, UNKNOWN, UPDATE, USER, USING, VALUES, VARCHAR, VARYING, VIEW, WHEN, WHERE, WITH, WORK, YEAR_P, ZONE *************** *** 558,563 **** --- 558,575 ---- $$->defname = "password"; $$->arg = (Node *)makeString($2); } + | ENCRYPTED PASSWORD Sconst + { + $$ = makeNode(DefElem); + $$->defname = "encryptedPassword"; + $$->arg = (Node *)makeString($3); + } + | UNENCRYPTED PASSWORD Sconst + { + $$ = makeNode(DefElem); + $$->defname = "unencryptedPassword"; + $$->arg = (Node *)makeString($3); + } | SYSID Iconst { $$ = makeNode(DefElem); *************** *** 5765,5770 **** --- 5777,5783 ---- | DISTINCT { $$ = "distinct"; } | DO { $$ = "do"; } | ELSE { $$ = "else"; } + | ENCRYPTED { $$ = "encrypted"; } | END_TRANS { $$ = "end"; } | EXCEPT { $$ = "except"; } | EXISTS { $$ = "exists"; } *************** *** 5836,5841 **** --- 5849,5855 ---- | TRANSACTION { $$ = "transaction"; } | TRIM { $$ = "trim"; } | TRUE_P { $$ = "true"; } + | UNENCRYPTED { $$ = "unencrypted"; } | UNION { $$ = "union"; } | UNIQUE { $$ = "unique"; } | UNKNOWN { $$ = "unknown"; } Index: src/backend/parser/keywords.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/parser/keywords.c,v retrieving revision 1.94 diff -c -r1.94 keywords.c *** src/backend/parser/keywords.c 2001/07/16 05:06:58 1.94 --- src/backend/parser/keywords.c 2001/08/15 05:13:02 *************** *** 102,107 **** --- 102,108 ---- {"each", EACH}, {"else", ELSE}, {"encoding", ENCODING}, + {"encrypted", ENCRYPTED}, {"end", END_TRANS}, {"escape", ESCAPE}, {"except", EXCEPT}, *************** *** 262,267 **** --- 263,269 ---- {"truncate", TRUNCATE}, {"trusted", TRUSTED}, {"type", TYPE_P}, + {"unencrypted", UNENCRYPTED}, {"union", UNION}, {"unique", UNIQUE}, {"unknown", UNKNOWN}, Index: src/backend/utils/misc/guc.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/utils/misc/guc.c,v retrieving revision 1.45 diff -c -r1.45 guc.c *** src/backend/utils/misc/guc.c 2001/07/05 15:19:40 1.45 --- src/backend/utils/misc/guc.c 2001/08/15 05:13:03 *************** *** 80,85 **** --- 80,87 ---- bool Australian_timezones = false; + bool Password_encryption = false; + #ifndef PG_KRB_SRVTAB #define PG_KRB_SRVTAB "" #endif *************** *** 246,255 **** {"sql_inheritance", PGC_USERSET, &SQL_inheritance, true, NULL}, ! {"australian_timezones", PGC_USERSET, &Australian_timezones, ! false, ClearDateCache}, {"fixbtree", PGC_POSTMASTER, &FixBTree, true, NULL}, {NULL, 0, NULL, false, NULL} }; --- 248,258 ---- {"sql_inheritance", PGC_USERSET, &SQL_inheritance, true, NULL}, ! {"australian_timezones", PGC_USERSET, &Australian_timezones, false, ClearDateCache}, {"fixbtree", PGC_POSTMASTER, &FixBTree, true, NULL}, + + {"password_encryption", PGC_USERSET, &Password_encryption, false, NULL}, {NULL, 0, NULL, false, NULL} }; Index: src/backend/utils/misc/postgresql.conf.sample =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/utils/misc/postgresql.conf.sample,v retrieving revision 1.15 diff -c -r1.15 postgresql.conf.sample *** src/backend/utils/misc/postgresql.conf.sample 2001/07/05 15:19:40 1.15 --- src/backend/utils/misc/postgresql.conf.sample 2001/08/15 05:13:03 *************** *** 176,184 **** # # Misc # - #default_transaction_isolation = 'read committed' - #sql_inheritance = true #australian_timezones = false #deadlock_timeout = 1000 #max_expr_depth = 10000 # min 10 --- 176,185 ---- # # Misc # #australian_timezones = false #deadlock_timeout = 1000 + #default_transaction_isolation = 'read committed' #max_expr_depth = 10000 # min 10 + #password_encryption = false + #sql_inheritance = true Index: src/include/miscadmin.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/miscadmin.h,v retrieving revision 1.88 diff -c -r1.88 miscadmin.h *** src/include/miscadmin.h 2001/08/05 02:06:50 1.88 --- src/include/miscadmin.h 2001/08/15 05:13:03 *************** *** 171,178 **** extern bool allowSystemTableMods; extern int SortMem; ! /* a few postmaster startup options are exported here so the ! configuration file processor has access to them */ extern bool NetServer; extern bool EnableSSL; --- 171,180 ---- extern bool allowSystemTableMods; extern int SortMem; ! /* ! * A few postmaster startup options are exported here so the ! * configuration file processor can access them. ! */ extern bool NetServer; extern bool EnableSSL; Index: src/include/libpq/crypt.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/libpq/crypt.h,v retrieving revision 1.11 diff -c -r1.11 crypt.h *** src/include/libpq/crypt.h 2000/07/04 16:32:01 1.11 --- src/include/libpq/crypt.h 2001/08/15 05:13:03 *************** *** 22,31 **** extern char *crypt_getpwdfilename(void); extern char *crypt_getpwdreloadfilename(void); ! #ifdef NOT_USED ! extern MsgType crypt_salt(const char *user); ! ! #endif ! extern int crypt_verify(const Port *port, const char *user, const char *pgpass); #endif --- 22,27 ---- extern char *crypt_getpwdfilename(void); extern char *crypt_getpwdreloadfilename(void); ! extern int md5_crypt_verify(const Port *port, const char *user, const char *pgpass); #endif Index: src/include/libpq/hba.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/libpq/hba.h,v retrieving revision 1.22 diff -c -r1.22 hba.h *** src/include/libpq/hba.h 2001/08/01 23:25:39 1.22 --- src/include/libpq/hba.h 2001/08/15 05:13:03 *************** *** 35,41 **** uaTrust, uaIdent, uaPassword, ! uaCrypt } UserAuth; typedef struct Port hbaPort; --- 35,43 ---- uaTrust, uaIdent, uaPassword, ! uaCrypt, ! uaMD5 /* This starts as uaCrypt from pg_hba.conf, but gets ! overridden if the client supports MD5 */ } UserAuth; typedef struct Port hbaPort; Index: src/include/libpq/pqcomm.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/libpq/pqcomm.h,v retrieving revision 1.55 diff -c -r1.55 pqcomm.h *** src/include/libpq/pqcomm.h 2001/03/22 04:00:48 1.55 --- src/include/libpq/pqcomm.h 2001/08/15 05:13:05 *************** *** 90,96 **** /* The earliest and latest frontend/backend protocol version supported. */ #define PG_PROTOCOL_EARLIEST PG_PROTOCOL(0,0) ! #define PG_PROTOCOL_LATEST PG_PROTOCOL(2,0) /* * All packets sent to the postmaster start with the length. This is omitted --- 90,96 ---- /* The earliest and latest frontend/backend protocol version supported. */ #define PG_PROTOCOL_EARLIEST PG_PROTOCOL(0,0) ! #define PG_PROTOCOL_LATEST PG_PROTOCOL(2,1) /* * All packets sent to the postmaster start with the length. This is omitted *************** *** 127,137 **** /* These are the authentication requests sent by the backend. */ ! #define AUTH_REQ_OK 0 /* User is authenticated */ #define AUTH_REQ_KRB4 1 /* Kerberos V4 */ #define AUTH_REQ_KRB5 2 /* Kerberos V5 */ #define AUTH_REQ_PASSWORD 3 /* Password */ ! #define AUTH_REQ_CRYPT 4 /* Encrypted password */ typedef uint32 AuthRequest; --- 127,138 ---- /* These are the authentication requests sent by the backend. */ ! #define AUTH_REQ_OK 0 /* User is authenticated */ #define AUTH_REQ_KRB4 1 /* Kerberos V4 */ #define AUTH_REQ_KRB5 2 /* Kerberos V5 */ #define AUTH_REQ_PASSWORD 3 /* Password */ ! #define AUTH_REQ_CRYPT 4 /* crypt password */ ! #define AUTH_REQ_MD5 5 /* md5 password */ typedef uint32 AuthRequest; Index: src/interfaces/libpq/Makefile =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/Makefile,v retrieving revision 1.53 diff -c -r1.53 Makefile *** src/interfaces/libpq/Makefile 2001/07/15 13:45:04 1.53 --- src/interfaces/libpq/Makefile 2001/08/15 05:13:05 *************** *** 20,26 **** override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) -DFRONTEND -DSYSCONFDIR='"$(sysconfdir)"' OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \ ! pqexpbuffer.o dllist.o pqsignal.o \ $(INET_ATON) $(SNPRINTF) $(STRERROR) ifdef MULTIBYTE --- 20,26 ---- override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) -DFRONTEND -DSYSCONFDIR='"$(sysconfdir)"' OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \ ! pqexpbuffer.o dllist.o md5.o pqsignal.o \ $(INET_ATON) $(SNPRINTF) $(STRERROR) ifdef MULTIBYTE *************** *** 33,39 **** SHLIB_LINK += $(filter -L%, $(LDFLAGS)) $(filter -lcrypt -ldes -lkrb -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket-lnsl -lresolv -lintl, $(LIBS)) ! all: all-lib # Shared library stuff include $(top_srcdir)/src/Makefile.shlib --- 33,39 ---- SHLIB_LINK += $(filter -L%, $(LDFLAGS)) $(filter -lcrypt -ldes -lkrb -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket-lnsl -lresolv -lintl, $(LIBS)) ! all: md5.c md5.h all-lib # Shared library stuff include $(top_srcdir)/src/Makefile.shlib *************** *** 49,54 **** --- 49,60 ---- dllist.c: $(backend_src)/lib/dllist.c rm -f $@ && $(LN_S) $< . + md5.c: $(backend_src)/libpq/md5.c + rm -f $@ && $(LN_S) $< . + + md5.h: $(backend_src)/../include/libpq/md5.h + rm -f $@ && $(LN_S) $< . + # this only gets done if configure finds system doesn't have inet_aton() inet_aton.c: $(backend_src)/port/inet_aton.c rm -f $@ && $(LN_S) $< . *************** *** 82,88 **** rm -f $(addprefix $(DESTDIR)$(includedir)/, libpq-fe.h libpq-int.h pqexpbuffer.h) clean distclean maintainer-clean: clean-lib ! rm -f $(OBJS) dllist.c wchar.c rm -f $(OBJS) inet_aton.c snprintf.c strerror.c depend dep: --- 88,94 ---- rm -f $(addprefix $(DESTDIR)$(includedir)/, libpq-fe.h libpq-int.h pqexpbuffer.h) clean distclean maintainer-clean: clean-lib ! rm -f $(OBJS) dllist.c md5.c md5.h wchar.c rm -f $(OBJS) inet_aton.c snprintf.c strerror.c depend dep: Index: src/interfaces/libpq/fe-auth.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v retrieving revision 1.48 diff -c -r1.48 fe-auth.c *** src/interfaces/libpq/fe-auth.c 2001/07/15 13:45:04 1.48 --- src/interfaces/libpq/fe-auth.c 2001/08/15 05:13:05 *************** *** 33,38 **** --- 33,39 ---- #include "libpq-fe.h" #include "libpq-int.h" #include "fe-auth.h" + #include "md5.h" #ifdef WIN32 #include "win32.h" *************** *** 434,445 **** static int pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq) { /* Encrypt the password if needed. */ ! if (areq == AUTH_REQ_CRYPT) ! password = crypt(password, conn->salt); ! return pqPacketSend(conn, password, strlen(password) + 1); } /* --- 435,486 ---- static int pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq) { + int ret; + char *crypt_pwd; + /* Encrypt the password if needed. */ ! switch (areq) ! { ! case AUTH_REQ_CRYPT: ! crypt_pwd = crypt(password, conn->salt); ! break; ! case AUTH_REQ_MD5: ! { ! char *crypt_pwd2; ! if (!(crypt_pwd = malloc(MD5_PASSWD_LEN+1)) || ! !(crypt_pwd2 = malloc(MD5_PASSWD_LEN+1))) ! { ! perror("malloc"); ! return STATUS_ERROR; ! } ! if (!EncryptMD5(password, conn->pguser, crypt_pwd2)) ! { ! free(crypt_pwd); ! free(crypt_pwd2); ! return STATUS_ERROR; ! } ! if (!EncryptMD5(crypt_pwd2 + strlen("md5"), conn->salt, ! crypt_pwd)) ! { ! free(crypt_pwd); ! free(crypt_pwd2); ! return STATUS_ERROR; ! } ! free(crypt_pwd2); ! break; ! } ! default: ! /* discard const so we can assign it */ ! crypt_pwd = (char *)password; ! break; ! } ! ! ret = pqPacketSend(conn, crypt_pwd, strlen(crypt_pwd) + 1); ! if (areq == AUTH_REQ_MD5) ! free(crypt_pwd); ! return ret; } /* *************** *** 494,499 **** --- 535,541 ---- case AUTH_REQ_PASSWORD: case AUTH_REQ_CRYPT: + case AUTH_REQ_MD5: if (password == NULL || *password == '\0') { (void) sprintf(PQerrormsg, *************** *** 506,514 **** "fe_sendauth: error sending password authentication\n"); return STATUS_ERROR; } - break; - default: snprintf(PQerrormsg, PQERRORMSG_LENGTH, libpq_gettext("authentication method %u not supported\n"), areq); --- 548,554 ---- Index: src/interfaces/libpq/fe-connect.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v retrieving revision 1.172 diff -c -r1.172 fe-connect.c *** src/interfaces/libpq/fe-connect.c 2001/08/03 22:11:39 1.172 --- src/interfaces/libpq/fe-connect.c 2001/08/15 05:13:06 *************** *** 1341,1347 **** } /* Get the password salt if there is one. */ ! if (areq == AUTH_REQ_CRYPT) { if (pqGetnchar(conn->salt, sizeof(conn->salt), conn)) { --- 1341,1347 ---- } /* Get the password salt if there is one. */ ! if (areq == AUTH_REQ_CRYPT || areq == AUTH_REQ_MD5) { if (pqGetnchar(conn->salt, sizeof(conn->salt), conn)) { *************** *** 1960,1966 **** closePGconn(PGconn *conn) { /* Note that the protocol doesn't allow us to send Terminate ! messages during the startup phase. */ if (conn->sock >= 0 && conn->status == CONNECTION_OK) { --- 1960,1966 ---- closePGconn(PGconn *conn) { /* Note that the protocol doesn't allow us to send Terminate ! messages during the startup phase. */ if (conn->sock >= 0 && conn->status == CONNECTION_OK) { Index: src/interfaces/libpq/fe-exec.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v retrieving revision 1.105 diff -c -r1.105 fe-exec.c *** src/interfaces/libpq/fe-exec.c 2001/08/03 22:11:39 1.105 --- src/interfaces/libpq/fe-exec.c 2001/08/15 05:13:13 *************** *** 1269,1275 **** static int getNotice(PGconn *conn) { - /* * Since the Notice might be pretty long, we create a temporary * PQExpBuffer rather than using conn->workBuffer. workBuffer is --- 1269,1274 ---- Index: src/interfaces/libpq/libpq-int.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v retrieving revision 1.36 diff -c -r1.36 libpq-int.h *** src/interfaces/libpq/libpq-int.h 2001/07/15 13:45:04 1.36 --- src/interfaces/libpq/libpq-int.h 2001/08/15 05:13:13 *************** *** 45,51 **** * pqcomm.h describe what the backend knows, not what libpq knows. */ ! #define PG_PROTOCOL_LIBPQ PG_PROTOCOL(2,0) /* * POSTGRES backend dependent Constants. --- 45,51 ---- * pqcomm.h describe what the backend knows, not what libpq knows. */ ! #define PG_PROTOCOL_LIBPQ PG_PROTOCOL(2,1) /* * POSTGRES backend dependent Constants. Index: src/interfaces/odbc/connection.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/odbc/connection.c,v retrieving revision 1.32 diff -c -r1.32 connection.c *** src/interfaces/odbc/connection.c 2001/06/19 02:17:06 1.32 --- src/interfaces/odbc/connection.c 2001/08/15 05:13:14 *************** *** 677,683 **** mylog("auth got 'R'\n"); areq = SOCK_get_int(sock, 4); ! if (areq == AUTH_REQ_CRYPT) SOCK_get_n_char(sock, salt, 2); mylog("areq = %d\n", areq); --- 677,683 ---- mylog("auth got 'R'\n"); areq = SOCK_get_int(sock, 4); ! if (areq == AUTH_REQ_CRYPT || areq == AUTH_REQ_MD5) SOCK_get_n_char(sock, salt, 2); mylog("areq = %d\n", areq); *************** *** 717,722 **** --- 717,723 ---- break; case AUTH_REQ_CRYPT: + case AUTH_REQ_MD5: self->errormsg = "Password crypt authentication not supported"; self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED; return 0; *************** *** 1672,1686 **** int CC_get_max_query_len(const ConnectionClass *conn) { ! int value; ! /* Long Queries in 7.0+ */ ! if (PG_VERSION_GE(conn, 7.0)) ! value = 0 /* MAX_STATEMENT_LEN */; ! /* Prior to 7.0 we used 2*BLCKSZ */ ! else if (PG_VERSION_GE(conn, 6.5)) ! value = (2 * BLCKSZ); ! else ! /* Prior to 6.5 we used BLCKSZ */ ! value = BLCKSZ; ! return value; } --- 1673,1687 ---- int CC_get_max_query_len(const ConnectionClass *conn) { ! int value; ! /* Long Queries in 7.0+ */ ! if (PG_VERSION_GE(conn, 7.0)) ! value = 0 /* MAX_STATEMENT_LEN */; ! /* Prior to 7.0 we used 2*BLCKSZ */ ! else if (PG_VERSION_GE(conn, 6.5)) ! value = (2 * BLCKSZ); ! else ! /* Prior to 6.5 we used BLCKSZ */ ! value = BLCKSZ; ! return value; } Index: src/interfaces/odbc/connection.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/odbc/connection.h,v retrieving revision 1.24 diff -c -r1.24 connection.h *** src/interfaces/odbc/connection.h 2001/05/30 20:52:34 1.24 --- src/interfaces/odbc/connection.h 2001/08/15 05:13:14 *************** *** 93,98 **** --- 93,99 ---- #define AUTH_REQ_KRB5 2 #define AUTH_REQ_PASSWORD 3 #define AUTH_REQ_CRYPT 4 + #define AUTH_REQ_MD5 5 /* Startup Packet sizes */ #define SM_DATABASE 64 /* * md5.c * * Implements the MD5 Message-Digest Algorithm as specified in * RFC 1321. This implementation is a simple one, in that it * needs every input byte to be buffered before doing any * calculations. I do not expect this file to be used for * general purpose MD5'ing of large amounts of data, only for * generating hashed passwords from limited input. * * Sverre H. Huseby <sverrehu@online.no> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include "postgres.h" #include "libpq/md5.h" /* * PRIVATE FUNCTIONS */ typedef unsigned char unsigned8; typedef unsigned int unsigned32; typedef unsigned long unsigned64; /* * The returned array is allocated using malloc. the caller should free it * when it is no longer needed. */ static unsigned8 * createPaddedCopyWithLength(unsigned8 *b, unsigned32 *l) { unsigned8 *ret; unsigned32 q; unsigned32 len, newLen448; unsigned64 len64; len = ((b == NULL) ? 0 : *l); newLen448 = len + 64 - (len % 64) - 8; if (newLen448 <= len) newLen448 += 64; *l = newLen448 + 8; if ((ret = (unsigned8 *) malloc(sizeof(unsigned8) * *l)) == NULL) return NULL; if (b != NULL) memcpy(ret, b, sizeof(unsigned8) * len); /* pad */ ret[len] = 0x80; for (q = len + 1; q < newLen448; q++) ret[q] = 0x00; /* append length as a 64 bit bitcount */ len64 = len; len64 <<= 3; q = newLen448; ret[q++] = (len64 & 0xFF); len64 >>= 8; ret[q++] = (len64 & 0xFF); len64 >>= 8; ret[q++] = (len64 & 0xFF); len64 >>= 8; ret[q++] = (len64 & 0xFF); len64 >>= 8; ret[q++] = (len64 & 0xFF); len64 >>= 8; ret[q++] = (len64 & 0xFF); len64 >>= 8; ret[q++] = (len64 & 0xFF); len64 >>= 8; ret[q] = (len64 & 0xFF); return ret; } #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define ROT_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) static void doTheRounds(unsigned32 X[16], unsigned32 state[4]) { unsigned32 a, b, c, d; a = state[0]; b = state[1]; c = state[2]; d = state[3]; /* round 1 */ a = b + ROT_LEFT((a + F(b, c, d) + X[ 0] + 0xd76aa478), 7); /* 1 */ d = a + ROT_LEFT((d + F(a, b, c) + X[ 1] + 0xe8c7b756), 12); /* 2 */ c = d + ROT_LEFT((c + F(d, a, b) + X[ 2] + 0x242070db), 17); /* 3 */ b = c + ROT_LEFT((b + F(c, d, a) + X[ 3] + 0xc1bdceee), 22); /* 4 */ a = b + ROT_LEFT((a + F(b, c, d) + X[ 4] + 0xf57c0faf), 7); /* 5 */ d = a + ROT_LEFT((d + F(a, b, c) + X[ 5] + 0x4787c62a), 12); /* 6 */ c = d + ROT_LEFT((c + F(d, a, b) + X[ 6] + 0xa8304613), 17); /* 7 */ b = c + ROT_LEFT((b + F(c, d, a) + X[ 7] + 0xfd469501), 22); /* 8 */ a = b + ROT_LEFT((a + F(b, c, d) + X[ 8] + 0x698098d8), 7); /* 9 */ d = a + ROT_LEFT((d + F(a, b, c) + X[ 9] + 0x8b44f7af), 12); /* 10 */ c = d + ROT_LEFT((c + F(d, a, b) + X[10] + 0xffff5bb1), 17); /* 11 */ b = c + ROT_LEFT((b + F(c, d, a) + X[11] + 0x895cd7be), 22); /* 12 */ a = b + ROT_LEFT((a + F(b, c, d) + X[12] + 0x6b901122), 7); /* 13 */ d = a + ROT_LEFT((d + F(a, b, c) + X[13] + 0xfd987193), 12); /* 14 */ c = d + ROT_LEFT((c + F(d, a, b) + X[14] + 0xa679438e), 17); /* 15 */ b = c + ROT_LEFT((b + F(c, d, a) + X[15] + 0x49b40821), 22); /* 16 */ /* round 2 */ a = b + ROT_LEFT((a + G(b, c, d) + X[ 1] + 0xf61e2562), 5); /* 17 */ d = a + ROT_LEFT((d + G(a, b, c) + X[ 6] + 0xc040b340), 9); /* 18 */ c = d + ROT_LEFT((c + G(d, a, b) + X[11] + 0x265e5a51), 14); /* 19 */ b = c + ROT_LEFT((b + G(c, d, a) + X[ 0] + 0xe9b6c7aa), 20); /* 20 */ a = b + ROT_LEFT((a + G(b, c, d) + X[ 5] + 0xd62f105d), 5); /* 21 */ d = a + ROT_LEFT((d + G(a, b, c) + X[10] + 0x02441453), 9); /* 22 */ c = d + ROT_LEFT((c + G(d, a, b) + X[15] + 0xd8a1e681), 14); /* 23 */ b = c + ROT_LEFT((b + G(c, d, a) + X[ 4] + 0xe7d3fbc8), 20); /* 24 */ a = b + ROT_LEFT((a + G(b, c, d) + X[ 9] + 0x21e1cde6), 5); /* 25 */ d = a + ROT_LEFT((d + G(a, b, c) + X[14] + 0xc33707d6), 9); /* 26 */ c = d + ROT_LEFT((c + G(d, a, b) + X[ 3] + 0xf4d50d87), 14); /* 27 */ b = c + ROT_LEFT((b + G(c, d, a) + X[ 8] + 0x455a14ed), 20); /* 28 */ a = b + ROT_LEFT((a + G(b, c, d) + X[13] + 0xa9e3e905), 5); /* 29 */ d = a + ROT_LEFT((d + G(a, b, c) + X[ 2] + 0xfcefa3f8), 9); /* 30 */ c = d + ROT_LEFT((c + G(d, a, b) + X[ 7] + 0x676f02d9), 14); /* 31 */ b = c + ROT_LEFT((b + G(c, d, a) + X[12] + 0x8d2a4c8a), 20); /* 32 */ /* round 3 */ a = b + ROT_LEFT((a + H(b, c, d) + X[ 5] + 0xfffa3942), 4); /* 33 */ d = a + ROT_LEFT((d + H(a, b, c) + X[ 8] + 0x8771f681), 11); /* 34 */ c = d + ROT_LEFT((c + H(d, a, b) + X[11] + 0x6d9d6122), 16); /* 35 */ b = c + ROT_LEFT((b + H(c, d, a) + X[14] + 0xfde5380c), 23); /* 36 */ a = b + ROT_LEFT((a + H(b, c, d) + X[ 1] + 0xa4beea44), 4); /* 37 */ d = a + ROT_LEFT((d + H(a, b, c) + X[ 4] + 0x4bdecfa9), 11); /* 38 */ c = d + ROT_LEFT((c + H(d, a, b) + X[ 7] + 0xf6bb4b60), 16); /* 39 */ b = c + ROT_LEFT((b + H(c, d, a) + X[10] + 0xbebfbc70), 23); /* 40 */ a = b + ROT_LEFT((a + H(b, c, d) + X[13] + 0x289b7ec6), 4); /* 41 */ d = a + ROT_LEFT((d + H(a, b, c) + X[ 0] + 0xeaa127fa), 11); /* 42 */ c = d + ROT_LEFT((c + H(d, a, b) + X[ 3] + 0xd4ef3085), 16); /* 43 */ b = c + ROT_LEFT((b + H(c, d, a) + X[ 6] + 0x04881d05), 23); /* 44 */ a = b + ROT_LEFT((a + H(b, c, d) + X[ 9] + 0xd9d4d039), 4); /* 45 */ d = a + ROT_LEFT((d + H(a, b, c) + X[12] + 0xe6db99e5), 11); /* 46 */ c = d + ROT_LEFT((c + H(d, a, b) + X[15] + 0x1fa27cf8), 16); /* 47 */ b = c + ROT_LEFT((b + H(c, d, a) + X[ 2] + 0xc4ac5665), 23); /* 48 */ /* round 4 */ a = b + ROT_LEFT((a + I(b, c, d) + X[ 0] + 0xf4292244), 6); /* 49 */ d = a + ROT_LEFT((d + I(a, b, c) + X[ 7] + 0x432aff97), 10); /* 50 */ c = d + ROT_LEFT((c + I(d, a, b) + X[14] + 0xab9423a7), 15); /* 51 */ b = c + ROT_LEFT((b + I(c, d, a) + X[ 5] + 0xfc93a039), 21); /* 52 */ a = b + ROT_LEFT((a + I(b, c, d) + X[12] + 0x655b59c3), 6); /* 53 */ d = a + ROT_LEFT((d + I(a, b, c) + X[ 3] + 0x8f0ccc92), 10); /* 54 */ c = d + ROT_LEFT((c + I(d, a, b) + X[10] + 0xffeff47d), 15); /* 55 */ b = c + ROT_LEFT((b + I(c, d, a) + X[ 1] + 0x85845dd1), 21); /* 56 */ a = b + ROT_LEFT((a + I(b, c, d) + X[ 8] + 0x6fa87e4f), 6); /* 57 */ d = a + ROT_LEFT((d + I(a, b, c) + X[15] + 0xfe2ce6e0), 10); /* 58 */ c = d + ROT_LEFT((c + I(d, a, b) + X[ 6] + 0xa3014314), 15); /* 59 */ b = c + ROT_LEFT((b + I(c, d, a) + X[13] + 0x4e0811a1), 21); /* 60 */ a = b + ROT_LEFT((a + I(b, c, d) + X[ 4] + 0xf7537e82), 6); /* 61 */ d = a + ROT_LEFT((d + I(a, b, c) + X[11] + 0xbd3af235), 10); /* 62 */ c = d + ROT_LEFT((c + I(d, a, b) + X[ 2] + 0x2ad7d2bb), 15); /* 63 */ b = c + ROT_LEFT((b + I(c, d, a) + X[ 9] + 0xeb86d391), 21); /* 64 */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; } static int calculateDigestFromBuffer(unsigned8 *b, unsigned32 len, unsigned8 sum[16]) { register unsigned32 i, j, k, newI; unsigned32 l; unsigned8 *input; register unsigned32 *wbp; unsigned32 workBuff[16], state[4]; l = len; state[0] = 0x67452301; state[1] = 0xEFCDAB89; state[2] = 0x98BADCFE; state[3] = 0x10325476; if ((input = createPaddedCopyWithLength(b, &l)) == NULL) return 0; for (i = 0;;) { if ((newI = i + 16 * 4) > l) break; k = i + 3; for (j = 0; j < 16; j++) { wbp = (workBuff + j); *wbp = input[k--]; *wbp <<= 8; *wbp |= input[k--]; *wbp <<= 8; *wbp |= input[k--]; *wbp <<= 8; *wbp |= input[k]; k += 7; } doTheRounds(workBuff, state); i = newI; } free(input); j = 0; for (i = 0; i < 4; i++) { k = state[i]; sum[j++] = (k & 0xFF); k >>= 8; sum[j++] = (k & 0xFF); k >>= 8; sum[j++] = (k & 0xFF); k >>= 8; sum[j++] = (k & 0xFF); } return 1; } static void bytesToHex(unsigned8 b[16], char *s) { static char *hex = "0123456789abcdef"; int q, w; for (q = 0, w = 0; q < 16; q++) { s[w++] = hex[(b[q] >> 4) & 0x0F]; s[w++] = hex[b[q] & 0x0F]; } s[w] = '\0'; } /* * PUBLIC FUNCTIONS */ /* * md5_hash * * Calculates the MD5 sum of the bytes in a buffer. * * SYNOPSIS #include "md5.h" * int md5_hash(const void *buff, size_t len, char *hexsum) * * INPUT buff the buffer containing the bytes that you want * the MD5 sum of. * len number of bytes in the buffer. * * OUTPUT hexsum the MD5 sum as a '\0'-terminated string of * hexadecimal digits. an MD5 sum is 16 bytes long. * each byte is represented by two heaxadecimal * characters. you thus need to provide an array * of 33 characters, including the trailing '\0'. * * RETURNS 0 on failure (out of memory for internal buffers) or * non-zero on success. * * STANDARDS MD5 is described in RFC 1321. * * AUTHOR Sverre H. Huseby <sverrehu@online.no> * */ bool md5_hash(const void *buff, size_t len, char *hexsum) { unsigned8 sum[16]; if (!calculateDigestFromBuffer((unsigned8 *) buff, len, sum)) return false; bytesToHex(sum, hexsum); return true; } /* * puts md5(username+passwd) in buf provided buflen is at least 36 bytes * returns 1 on success, 0 on any kind of failure and sets errno accordingly */ bool EncryptMD5(const char *passwd, const char *salt, char *buf) { char crypt_buf[128]; if (strlen(salt) + strlen(passwd) > 127) return false; strcpy(buf, "md5"); memset(crypt_buf, 0, 128); sprintf(crypt_buf,"%s%s", salt, passwd); return md5_hash(crypt_buf, strlen(crypt_buf), buf + 3); } /*------------------------------------------------------------------------- * * md5.h * Interface to hba.c * * *------------------------------------------------------------------------- */ #ifndef PG_MD5_H #define PG_MD5_H extern bool md5_hash(const void *buff, size_t len, char *hexsum); extern bool CheckMD5Pwd(char *passwd, char *storedpwd, char *seed); extern bool EncryptMD5(const char *passwd, const char *salt, char *buf); #define MD5_PASSWD_LEN 35 #define isMD5(passwd) (strncmp((passwd),"md5",3) == 0 && \ strlen(passwd) == MD5_PASSWD_LEN) #endif
pgsql-patches by date: