[tpop3d-discuss] 1.4.2 auth_mysql patch

Stephen Friedl steve at unixwiz.net
Sun, 29 Dec 2002 22:02:02 -0800


Hello list,

This is a patch to tpop3d-1.4.2 to fix a fatal error in the mySQL
module. The current code does not check to insure that the data from
either of:

	auth-mysql-pass-query

	auth-mysql-apop-query

are not NULL - if this happens, tpop3d simply exits with a SIGSEGV,
and this is not very helpful (no logging is done). I've added a
"valid_mysql_params()" function that checks the four params in both
places and logs a more helpful message when NULL is found. I also added
local temp variables for row[0] .. row[3] so their purpose was a bit
more clear.

Both these two routines (APOP and PASS) share a large amount of common
code, so it probably makes sense to do some merging. But I decided this
was outside my portfolio for the evening.

Also a trivial addition to signals.c (added <memory.h>)

Hope this fits in the proper tpo3pd style...

--- 
Stephen J Friedl | Software Consultant | Tustin, CA |   +1 714 544-6561
www.unixwiz.net  | I speak for me only |   KA8CMY   | steve@unixwiz.net


Patch created:  Sun Dec 29 21:59:30 PST 2002
Working dir..:  /source/tpop3d-1.4.2

Patch created with:
=======================================================
{
	cat ../tpop3d-patch-header

	echo ""

	echo "Patch created: " `date`
	echo "Working dir..: " `pwd`

	echo ""

	echo "Patch created with:"

	echo "======================================================="

	cat ../make-tpop3d-patch

	echo "======================================================="

	echo ""

	LC_ALL=C TZ=UTC0 diff -Naur auth_mysql.c.orig auth_mysql.c
	LC_ALL=C TZ=UTC0 diff -Naur signals.c.orig signals.c
}
=======================================================

--- auth_mysql.c.orig	Mon Dec 30 04:32:34 2002
+++ auth_mysql.c	Mon Dec 30 05:41:56 2002
@@ -210,6 +210,56 @@
 
 /* MD5 crypt(3) routines end. */
 
+/*
+ * valid_mysql_params()
+ *
+ *      Given the four parameters fetched from mysql for a user, either
+ *      for regular or APOP passwords, make sure none is NULL. Return
+ *      TRUE if all is well or FALSE if not, but we take care to output
+ *      a message indicating what's wrong. The first param is the name 
+ *      of the 
+ */
+
+static int valid_mysql_params(const char *caller, const char *name,
+                              const char *ROW_mboxpath,
+                              const char *ROW_passhash,
+                              const char *ROW_unixuser,
+                              const char *ROW_mboxtype )
+{
+        if ( ROW_mboxpath == 0 )
+        {
+            log_print(LOG_WARNING,
+                _("%s: mboxpath is NULL for user {%s}"),
+                caller, name);
+            return 0;
+        }
+        
+        if ( ROW_passhash == 0 )
+        {
+            log_print(LOG_WARNING,
+                _("%s: passhash is NULL for user '%s'"),
+                caller, name);
+            return 0;
+        }
+        
+        if ( ROW_unixuser == 0 )
+        {
+            log_print(LOG_WARNING,
+                _("%s: unixuser is NULL for user '%s'"),
+                caller, name);
+            return 0;
+        }
+        
+        if ( ROW_mboxtype == 0 )
+        {
+            log_print(LOG_WARNING,
+                _("%s: mboxtype is NULL for user '%s'"),
+                caller, name);
+            return 0;
+        }
+        return 1;
+}
+
 
 /* MySQL PASSWORD() routines. This is here so that you can use the MySQL
  * proprietary password-hashing routine with tpop3d. The code is inserted here
@@ -399,13 +449,26 @@
                 unsigned char this_digest[16];
                 MD5_CTX ctx;
                 uid_t uid;
+                const char *ROW_mboxpath;       /* full path of mailbox */
+                const char *ROW_passhash;       /* hashed password */
+                const char *ROW_unixuser;       /* UNIX user name/number */
+                const char *ROW_mboxtype;       /* 'maildir' or 'bsd' */
                 
                 row = mysql_fetch_row(result);
                 /* These are "can't happen" errors */
                 if (!row || !(lengths = mysql_fetch_lengths(result))) break;
 
+                if ( ! valid_mysql_params("auth_mysql_new_apop", who,
+                                          ROW_mboxpath = row[0],
+                                          ROW_passhash = row[1],
+                                          ROW_unixuser = row[2],
+                                          ROW_mboxtype = row[3]) )
+                {
+                    break;
+                }
+
                 /* Verify that this user has a plaintext password. */
-                if (strncmp(row[1], "{plaintext}", 11) != 0) {
+                if (strncmp(ROW_passhash, "{plaintext}", 11) != 0) {
                     log_print(LOG_WARNING, _("auth_mysql_new_apop: attempted APOP login by %s, who does not have a plaintext password"), who);
                     break;
                 }
@@ -413,7 +476,7 @@
                 /* Calculate our idea of the digest */
                 MD5Init(&ctx);
                 MD5Update(&ctx, (unsigned char*)timestamp, strlen(timestamp));
-                MD5Update(&ctx, (unsigned char*)row[1] + 11, lengths[1] - 11);
+                MD5Update(&ctx, (unsigned char*)ROW_passhash  + 11, lengths[1] - 11);
                 MD5Final(this_digest, &ctx);
 
                 /* User was lying */
@@ -423,8 +486,8 @@
                 }
 
                 /* User was not lying (about her password) */
-                if (!parse_uid((const char*)row[2], &uid)) {
-                    log_print(LOG_ERR, _("auth_mysql_new_apop: unix user `%s' for %s does not make sense"), row[3], who);
+                if (!parse_uid(ROW_unixuser, &uid)) {
+                    log_print(LOG_ERR, _("auth_mysql_new_apop: unix user `%s' for %s does not make sense"), ROW_unixuser, who);
                     break;
                 }
 
@@ -435,7 +498,7 @@
                     break;
                 }
 
-                a = authcontext_new(pw->pw_uid, use_gid ? mail_gid : pw->pw_gid, row[3], row[0], pw->pw_dir);
+                a = authcontext_new(pw->pw_uid, use_gid ? mail_gid : pw->pw_gid, ROW_mboxtype, ROW_mboxpath, pw->pw_dir);
 
                 break;
             }
@@ -501,25 +564,38 @@
         case 1: {
                 MYSQL_ROW row;
                 unsigned long *lengths;
-                char *pwhash;
+                const char *pwhash;
                 struct passwd *pw;
                 int authok = 0;
                 uid_t uid;
+                const char *ROW_mboxpath;       /* full path of mailbox */
+                const char *ROW_passhash;       /* hashed password */
+                const char *ROW_unixuser;       /* UNIX user name/number */
+                const char *ROW_mboxtype;       /* 'maildir' or 'bsd' */
                 
                 row = mysql_fetch_row(result);
 
                 /* These are "can't happen" errors */
                 if (!row || !(lengths = mysql_fetch_lengths(result))) break;
 
+                if ( ! valid_mysql_params("auth_mysql_new_user_pass", who,
+                                          ROW_mboxpath = row[0],
+                                          ROW_passhash = row[1],
+                                          ROW_unixuser = row[2],
+                                          ROW_mboxtype = row[3]) )
+                {
+                    break;
+                }
+
                 /* Verify the password. There are several possibilities here. */
-                pwhash = (char*)row[1];
+                pwhash = ROW_passhash;
 
                 if (strncmp(pwhash, "{crypt}", 7) == 0) {
                     /* Password hashed by system crypt function. */
                     if (strcmp(crypt(pass, pwhash + 7), pwhash + 7) == 0) authok = 1;
                 } else if (strncmp(pwhash, "{crypt_md5}", 11) == 0) {
                     /* Password hashed by crypt_md5. */
-                    if (strcmp(crypt_md5(pass, pwhash + 11), pwhash + 11) == 0) authok = 1;
+                    if (strcmp(crypt_md5(pass, pwhash + 10), pwhash + 11) == 0) authok = 1;
                 } else if (strncmp(pwhash, "{plaintext}", 11) == 0) {
                     /* Plain text password, as used for APOP. */
                     if (strcmp(pass, pwhash + 11) == 0) authok = 1;
@@ -571,8 +647,8 @@
                     break;
                 }
 
-                if (!parse_uid((const char*)row[2], &uid)) {
-                    log_print(LOG_ERR, _("auth_mysql_new_user_pass: unix user `%s' for %s does not make sense"), row[3], who);
+                if (!parse_uid((const char*)ROW_unixuser, &uid)) {
+                    log_print(LOG_ERR, _("auth_mysql_new_user_pass: unix user `%s' for %s does not make sense"), ROW_unixuser, who);
                     break;
                 }
 
@@ -583,7 +659,7 @@
                     break;
                 }
 
-                a = authcontext_new(pw->pw_uid, use_gid ? mail_gid : pw->pw_gid, row[3], row[0], pw->pw_dir);
+                a = authcontext_new(pw->pw_uid, use_gid ? mail_gid : pw->pw_gid, ROW_mboxtype, ROW_mboxpath, pw->pw_dir);
                 break;
             }
 
--- signals.c.orig	Mon Dec 30 04:22:35 2002
+++ signals.c	Mon Dec 30 04:54:52 2002
@@ -15,6 +15,7 @@
 #include <errno.h>
 #include <signal.h>
 #include <syslog.h>
+#include <memory.h>
 
 #include <sys/wait.h>