diff -urN pam_mysql/pam_mysql.c pam_mysql.sella/pam_mysql.c --- pam_mysql/pam_mysql.c Thu Sep 7 08:24:07 2000 +++ pam_mysql.sella/pam_mysql.c Sun Sep 24 08:52:05 2000 @@ -127,11 +127,13 @@ { char *sql; char *escapeUser; /* User provided stuff MUST be escaped */ - char *escapePass; /* User provided stuff MUST be escaped */ + char *encryptedPass; + char *salt; #ifdef DEBUG char *sqllog; #endif MYSQL_RES *result; + MYSQL_ROW row; int retvalue = PAM_AUTH_ERR; #ifdef DEBUG D (("called.")); @@ -141,51 +143,29 @@ if ( !sql ) return PAM_BUF_ERR; - /* We can check the password with the SQL query in all cases, and - then we don't need to check anything later - only number of - rows in result ; - this way we fix the problem with PASSWORD (crypt=2) - it - was not functional before :( ; - we can use MySQL function ENCRYPT (crypt=1), which calls system - crypt() and we don't need to call it later ; - Martin "Edas" Edlman, August 18 2000 */ + /* To avoid putting a plain password in the MySQL log file and on the wire + more than needed we will request the encrypted password from MySQL. We + will check encrypt the passed password against the oen returned from MySQL. + */ escapeUser = malloc(sizeof(char) * (strlen(user) * 2) + 1); - escapePass = malloc(sizeof(char) * (strlen(passwd) * 2) + 1); - if ( escapeUser == NULL || escapePass == NULL ) + if ( escapeUser == NULL ) { - syslog(LOG_ERR, "pam_mysql: Insufficient memory to allocate user or password escape strings"); + syslog(LOG_ERR, "pam_mysql: Insufficient memory to allocate user escape string"); syslog(LOG_ERR, "pam_mysql: UNABLE TO AUTHENTICATE"); return PAM_BUF_ERR; } #ifdef HAVE_MYSQL_REAL_ESCAPE_STRING mysql_real_escape_string(auth_sql_server, escapeUser, user, strlen(user)); - mysql_real_escape_string(auth_sql_server, escapePass, passwd, strlen(passwd)); #else mysql_escape_string(escapeUser, user, strlen(user)); - mysql_escape_string(escapePass, passwd, strlen(passwd)); #endif - switch(options.crypt) { - case 0: sprintf(sql, "select %s from %s where %s='%s' and %s='%s'", - options.usercolumn,options.table, - options.usercolumn,escapeUser, - options.passwdcolumn,escapePass); - break; - case 1: sprintf(sql, "select %s from %s where %s='%s' and %s=ENCRYPT('%s',LEFT(%s,2))", - options.usercolumn,options.table, - options.usercolumn,escapeUser, - options.passwdcolumn,escapePass,options.passwdcolumn); - break; - case 2: sprintf(sql, "select %s from %s where %s='%s' and %s=PASSWORD('%s')", - options.usercolumn,options.table, - options.usercolumn,escapeUser, - options.passwdcolumn,escapePass); - break; - default: sprintf(sql, "generate_sql_error"); - } + sprintf(sql, "SELECT %s FROM %s WHERE %s='%s'", + options.passwdcolumn,options.table, + options.usercolumn,escapeUser); if ( strlen(options.where) > 0 ) { - sprintf(sql, "%s and %s", sql, options.where); + sprintf(sql, "%s AND %s", sql, options.where); } #ifdef DEBUG syslog(LOG_ERR, "pam_mysql: where clause = %s", options.where); @@ -213,15 +193,44 @@ syslog(LOG_ERR, mysql_error (auth_sql_server)); return PAM_AUTH_ERR; } - /* the password was checked by SQL query, so we only check if the - result set contains at least one row - I think it should contain - exactly one row - Martin "Edas" Edlman, August 18 2000 */ - /* I agree, so I've modified it accordingly. - Steve 5th Sep 2000 - */ if (mysql_num_rows (result) == 1) { - retvalue = PAM_SUCCESS; + encryptedPass = malloc(sizeof(char) * (strlen(passwd) + 31) + 1); + /* Grab the password from RESULT_SET. */ + row = mysql_fetch_row(result); + if (row == NULL) { + syslog(LOG_ERR, mysql_error (auth_sql_server)); + return PAM_AUTH_ERR; + } + switch(options.crypt) { + /* PLAIN */ + case 0: strcpy(encryptedPass, passwd); + break; + /* ENCRYPT */ + case 1: if (strlen(row[0]) < 12) { /* strlen() < 12 isn't a valid encrypted password. */ + syslog(LOG_ERR, "pam_mysql: select returned an invalid encrypted password" ); + break; + } + salt = malloc(sizeof(char) * strlen(row[0]) + 1); + if (strncmp("$1$", row[0], 3) == 0) { /* A MD5 salt starts with "$1$" and is 12 bytes long. */ + strncpy(salt, row[0], 12); + salt[12] = '\0'; + } else { /* If it's not MD5, assume DES and a 2 bytes salt. */ + strncpy(salt, row[0], 2); + salt[2] = '\0'; + } + strcpy(encryptedPass, crypt(passwd, salt)); + free (salt); + break; + /* PASSWORD */ + case 2: make_scrambled_password(encryptedPass, passwd); + break; + } + if (!strcmp(row[0], encryptedPass)) { + retvalue = PAM_SUCCESS; + } + free (encryptedPass); + } else { + syslog(LOG_ERR, "pam_mysql: select returned more than one result"); } #ifdef DEBUG D (("returning %i.",retvalue));