From: UnyfiOnGithub <kyrian@ore.org>
Date: Sat, 29 Aug 2020 21:17:13 +0100
Subject: apply patch for missing 323 code in more modern mysql client libs
 and appropriate readme warnings

---
 README                |   5 +++
 compat_323_password.h | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++
 pam_mysql.c           |  20 +++++++++-
 3 files changed, 129 insertions(+), 2 deletions(-)
 create mode 100644 compat_323_password.h

diff --git a/README b/README
index 1408fda..94a61eb 100644
--- a/README
+++ b/README
@@ -144,6 +144,11 @@ use_323_passwd (false)
 
     This option appeared since 0.7pre2 and 0.6.1.
 
+    Note that the code for this to work has been dropped from client libraries
+    for mysql 5.x (or so) onwards, so a workaround has been patched in to
+    pam-mysql. If at all possible you should upgrade your password encryption
+    method instead and not rely on this feature.
+
 where
 
     Additional criteria for the query. For example:
diff --git a/compat_323_password.h b/compat_323_password.h
new file mode 100644
index 0000000..9807d07
--- /dev/null
+++ b/compat_323_password.h
@@ -0,0 +1,106 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/* password checking routines */
+/*****************************************************************************
+  The main idea is that no password are sent between client & server on
+  connection and that no password are saved in mysql in a decodable form.
+
+  On connection a random string is generated and sent to the client.
+  The client generates a new string with a random generator inited with
+  the hash values from the password and the sent string.
+  This 'check' string is sent to the server where it is compared with
+  a string generated from the stored hash_value of the password and the
+  random string.
+
+  The password is saved (in user.password) by using the PASSWORD() function in
+  mysql.
+
+  This is .c file because it's used in libmysqlclient, which is entirely in C.
+  (we need it to be portable to a variety of systems).
+  Example:
+    update user set password=PASSWORD("hello") where user="test"
+  This saves a hashed number as a string in the password field.
+
+  The new authentication is performed in following manner:
+
+  SERVER:  public_seed=create_random_string()
+           send(public_seed)
+
+  CLIENT:  recv(public_seed)
+           hash_stage1=sha1("password")
+           hash_stage2=sha1(hash_stage1)
+           reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
+
+           // this three steps are done in scramble() 
+
+           send(reply)
+
+     
+  SERVER:  recv(reply)
+           hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
+           candidate_hash2=sha1(hash_stage1)
+           check(candidate_hash2==hash_stage2)
+
+           // this three steps are done in check_scramble()
+
+*****************************************************************************/
+
+
+/*
+    Generate binary hash from raw text string 
+    Used for Pre-4.1 password handling
+  SYNOPSIS
+    hash_password()
+    result       OUT store hash in this location
+    password     IN  plain text password to build hash
+    password_len IN  password length (password may be not null-terminated)
+*/
+
+void compat_323_hash_password(ulong *result, const char *password, uint password_len)
+{
+  register ulong nr=1345345333L, add=7, nr2=0x12345671L;
+  ulong tmp;
+  const char *password_end= password + password_len;
+  for (; password < password_end; password++)
+  {
+    if (*password == ' ' || *password == '\t')
+      continue;                                 /* skip space in password */
+    tmp= (ulong) (unsigned char) *password;
+    nr^= (((nr & 63)+add)*tmp)+ (nr << 8);
+    nr2+=(nr2 << 8) ^ nr;
+    add+=tmp;
+  }
+  result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */;
+  result[1]=nr2 & (((ulong) 1L << 31) -1L);
+}
+
+
+/*
+    Create password to be stored in user database from raw string
+    Used for pre-4.1 password handling
+  SYNOPSIS
+    make_scrambled_password_323()
+    to        OUT store scrambled password here
+    password  IN  user-supplied password
+*/
+
+void compat_make_scrambled_password_323(char *to, const char *password)
+{
+  ulong hash_res[2];
+  compat_323_hash_password(hash_res, password, (uint) strlen(password));
+  sprintf(to, "%08lx%08lx", hash_res[0], hash_res[1]);
+}
diff --git a/pam_mysql.c b/pam_mysql.c
index f4dac29..593f4ca 100644
--- a/pam_mysql.c
+++ b/pam_mysql.c
@@ -3724,12 +3724,20 @@ static pam_mysql_err_t pam_mysql_check_passwd(pam_mysql_ctx_t *ctx,
                                 char buf[42];
 #ifdef HAVE_MAKE_SCRAMBLED_PASSWORD_323
                                 if (ctx->use_323_passwd) {
+                                    syslog(LOG_DEBUG, PAM_MYSQL_LOG_PREFIX "use_323_passwd defined and enabled in pam_mysql_check_passwd");
                                     make_scrambled_password_323(buf, passwd);
                                 } else {
+                                    syslog(LOG_DEBUG, PAM_MYSQL_LOG_PREFIX "use_323_passwd defined and not enabled in pam_mysql_check_passwd");
                                     make_scrambled_password(buf, passwd);
                                 }
 #else
-                                make_scrambled_password(buf, passwd);
+                                if (ctx->use_323_passwd) {
+                                    syslog(LOG_WARNING, PAM_MYSQL_LOG_PREFIX "Workaround applied. use_323_passwd not defined but use attempted in pam_mysql_check_passwd");
+                                    compat_make_scrambled_password_323(buf, passwd);
+                                } else {
+                                    syslog(LOG_DEBUG, PAM_MYSQL_LOG_PREFIX "use_323_passwd not defined and use not attempted in pam_mysql_check_passwd");
+                                    make_scrambled_password(buf, passwd);
+                                }
 #endif
 
                                 vresult = strcmp(row[0], buf);
@@ -4048,12 +4056,20 @@ static pam_mysql_err_t pam_mysql_update_passwd(pam_mysql_ctx_t *ctx, const char
                     }
 #ifdef HAVE_MAKE_SCRAMBLED_PASSWORD_323
                     if (ctx->use_323_passwd) {
+                        syslog(LOG_DEBUG, PAM_MYSQL_LOG_PREFIX "use_323_passwd defined and enabled in pam_mysql_update_passwd");
                         make_scrambled_password_323(encrypted_passwd, new_passwd);
                     } else {
+                        syslog(LOG_DEBUG, PAM_MYSQL_LOG_PREFIX "use_323_passwd defined and not enabled in pam_mysql_update_passwd");
                         make_scrambled_password(encrypted_passwd, new_passwd);
                     }
 #else
-                    make_scrambled_password(encrypted_passwd, new_passwd);
+                    if (ctx->use_323_passwd) {
+                        syslog(LOG_WARNING, PAM_MYSQL_LOG_PREFIX "Workaround applied. use_323_passwd not defined but use attempted in pam_mysql_update_passwd");
+                        compat_make_scrambled_password_323(encrypted_passwd, new_passwd);
+                    } else {
+                        syslog(LOG_DEBUG, PAM_MYSQL_LOG_PREFIX "use_323_passwd not defined and use not attempted in pam_mysql_update_passwd");
+                        make_scrambled_password(encrypted_passwd, new_passwd);
+                    }
 #endif
                     break;
 
