[tpop3d-discuss] signal 11?

Nancy Pettigrew (work) nancy at bigfishmail.com
Tue, 17 Jul 2001 16:43:18 -0700


Why would the process get the SIGSEGV after returning an error back from the
auth_mysql_new_user_pass function?
I've seen it happen in a few different instances, this is the only log
instance where some other error was reported prior to the signal 11:

Jul 17 21:49:36 mail1 tpop3d[30213]: auth_mysql_new_user_pass:
mysql_store_result: Lost connection to MySQL server during query
Jul 17 21:49:36 mail1 tpop3d[30213]: quit: signal 11

In other instances of the signal 11 logging, the auth_mysql_new_user_pass
function didn't log any errors.  The server then hung up for a few minutes,
not properly accepting connections, and then began accepting connections
again.

I tweaked the auth_mysql_new_user_pass function to do an additional query
for my setup, and I'm wondering if there's something I've missed that a
subsequent process needs...basically, I've changed it to use different
tables, and to do an additional query to translate domains for a domain
aliasing situation on the system.  I've gone through it several times,
perhaps I'm having a proofreading mental block, I don't know.  Any input
would be greatly appreciated.

Thanks,
Nancy

I'm using version 1.3.3.
Here's my tweaked version of auth_mysql_new_user_pass:

/* auth_mysql_new_user_pass:
 */
char user_pass_query_template[] =
    "SELECT distinct '/var/spool/mail', m.mail_location, m.user_password,
'mail' "
      "FROM users m LEFT JOIN aliases AS a ON m.user_id = a.user_id "
     "WHERE (a.address='%s@%s' OR m.primary_email = '%s@%s') AND DateDeleted
IS NULL";

char domain_trans_query_template[] =
    "SELECT new_domain "
      "FROM domain_translate "
     "WHERE domain = '%s'";

authcontext auth_mysql_new_user_pass(const char *user, const char *pass) {
    char *query, *x, *y, *dt_query;
    authcontext a = NULL;
    char *local_part;
    char *domain;
    char *p;
    unsigned char *q;
    item *I;
    size_t l, dt_l;
    int use_gid = 0;
    gid_t gid;

    if (!mysql) return NULL;

    /* Obtain gid to use */
    if ((I = stringmap_find(config, "auth-mysql-mail-group"))) {
        if (!parse_gid((char*)I->v, &gid)) {
            print_log(LOG_ERR, _("auth_mysql_new_user_pass:
auth-mysql-mail-group directive `%s' does not make sense"), (char*)I->v);
            return NULL;
        }
        use_gid = 1;
    }

    domain = user + strcspn(user, "@%!");
    if (domain == user || !*domain) return NULL;
    ++domain;

    local_part = (char*)malloc(domain - user);
    if (!local_part) return NULL;
    memset(local_part, 0, domain - user);
    strncpy(local_part, user, domain - user - 1);

    if (mysql_ping(mysql) == -1) {
        print_log(LOG_ERR, "auth_mysql_new_user_pass: mysql_ping: %s",
mysql_error(mysql));
        return NULL;
    }

    /* get domain_translate, if applicable */
    dt_query = (char*)malloc(dt_l = (sizeof(domain_trans_query_template) +
strlen(domain) + 1));
    if (!dt_query) goto fail;

    snprintf(dt_query, dt_l, domain_trans_query_template, domain);

    if (mysql_query(mysql, dt_query) == 0) {
        MYSQL_RES *dt_result = mysql_store_result(mysql);
        int j;

        if (!dt_result) {
            print_log(LOG_ERR, "auth_mysql_new_user_pass:
mysql_store_result: %s", mysql_error(mysql));
            goto fail;
        }

        switch (j = mysql_num_rows(dt_result)) {
        case 0:
            break;
        case 1: {
            MYSQL_ROW dt_row = mysql_fetch_row(dt_result);
            domain = (char*)dt_row[0];
            break;
            }
        default:
            print_log(LOG_ERR, _("auth_mysql_new_user_pass: database
inconsistency: query returned %d rows"), j);
            break;
        }
        mysql_free_result(dt_result);
    } else {
        print_log(LOG_ERR, "auth_mysql_new_user_pass: mysql_query: %s",
mysql_error(mysql));
        goto fail;
    }

    query = (char*)malloc(l = (sizeof(user_pass_query_template) +
(strlen(local_part) * 4) + (strlen(domain) * 4)));
    x = (char*)malloc(strlen(local_part) * 2 + 1);
    y = (char*)malloc(strlen(domain) * 2 + 1);
    if (!query || !x || !y) goto fail;
    mysql_escape_string(x, local_part, strlen(local_part));
    mysql_escape_string(y, domain, strlen(domain));
    snprintf(query, l, user_pass_query_template, x, y, x, y);

    if (mysql_query(mysql, query) == 0) {
        MYSQL_RES *result = mysql_store_result(mysql);
        int i;

        if (!result) {
            print_log(LOG_ERR, "auth_mysql_new_user_pass:
mysql_store_result: %s", mysql_error(mysql));
            goto fail;
        }

        switch (i = mysql_num_rows(result)) {
        case 0:
            break;
        case 1: {
                MYSQL_ROW row = mysql_fetch_row(result);
                unsigned long *lengths;
                char *pwhash;
                char *mailbox;
                struct passwd *pw;
                int authok = 0;
                uid_t uid;

                /* These are "can't happen" errors */
                if (!row || !(lengths = mysql_fetch_lengths(result))) break;

                /* Verify the password. There are several possibilities
here. */
                pwhash = (char*)row[2];

                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;
                } else if (strncmp(pwhash, "{plaintext}", 11) == 0) {
                    /* Plain text password, as used for APOP. */
                    if (strcmp(pass, pwhash + 11) == 0) authok = 1;
                } else if (strncmp(pwhash, "{md5}", 4) == 0 || *pwhash !=
'{') {
                    /* Straight MD5 password. */
                    MD5_CTX ctx;
                    unsigned char digest[16];
                    char hexhash[33] = {0};
                    MD5Init(&ctx);
                    MD5Update(&ctx, (unsigned char*)pass, strlen(pass));
                    MD5Final(digest, &ctx);

                    for (p = hexhash, q = digest; q < digest + 16; ++q, p +=
2) snprintf(p, 3, "%02x", (unsigned)*q);

                    if (strcasecmp(hexhash, pwhash + 5) == 0 ||
strcasecmp(hexhash, pwhash) == 0) authok = 1;
                } else {
                    /* Unknown format. */
                    print_log(LOG_ERR, _("auth_mysql_new_user_pass: %s@%s
has unknown password format `%.*s'"), local_part, domain, 2 + strcspn(pwhash
+ 1, "}"), pwhash);
                    break;
                }

                if (!authok) {
                    print_log(LOG_ERR, _("auth_mysql_new_user_pass: %s@%s
failed login with wrong password"), local_part, domain);
                    break;
                }

                if (!parse_uid((const char*)row[3], &uid)) {
                    print_log(LOG_ERR, _("auth_mysql_new_user_pass: unix
user `%s' for %s@%s does not make sense"), row[3], local_part, domain);
                    break;
                }

                pw = getpwuid(uid);

                if (!pw) {
                    print_log(LOG_ERR, "auth_mysql_new_user_pass:
getpwuid(%d): %m", (int)uid);
                    break;
                }

                mailbox = (char*)malloc(l = (lengths[0] + lengths[1] + 2));
                snprintf(mailbox, l, "%s%s", row[0], row[1]);
                a = authcontext_new(pw->pw_uid, use_gid ? gid : pw->pw_gid,
                                    NULL, mailbox, NULL, domain); /* note
default mailbox type. */
                free(mailbox);
                break;
            }

        default:
            print_log(LOG_ERR, _("auth_mysql_new_user_pass: database
inconsistency: query for %s@%s returned %d rows"), local_part, domain, i);
            break;
        }

        mysql_free_result(result);
    } else {
        print_log(LOG_ERR, "auth_mysql_new_user_pass: mysql_query: %s",
mysql_error(mysql));
    }
fail:
    if (local_part) free(local_part);
    if (x) free(x);
    if (y) free(y);
    if (query) free(query);
    if (dt_query) free(dt_query);

    return a;
}