Add missing principal name verification to GSSAPIAuth

The authenticator module did not compare the username with the principal
name that was given by the GSSAPI server.
This commit is contained in:
Markus Mäkelä 2017-01-27 16:11:30 +02:00
parent b5fba7c2d0
commit 5972d87fba

View File

@ -31,16 +31,16 @@
/** Query that gets all users that authenticate via the gssapi plugin */
const char *gssapi_users_query =
"SELECT u.user, u.host, d.db, u.select_priv FROM "
"SELECT u.user, u.host, d.db, u.select_priv, u.authentication_string FROM "
"mysql.user AS u LEFT JOIN mysql.db AS d "
"ON (u.user = d.user AND u.host = d.host) WHERE u.plugin = 'gssapi' "
"UNION "
"SELECT u.user, u.host, t.db, u.select_priv FROM "
"SELECT u.user, u.host, t.db, u.select_priv, u.authentication_string FROM "
"mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
"ON (u.user = t.user AND u.host = t.host) WHERE u.plugin = 'gssapi' "
"ORDER BY user";
#define GSSAPI_USERS_QUERY_NUM_FIELDS 4
#define GSSAPI_USERS_QUERY_NUM_FIELDS 5
/**
* SQLite queries for authenticating users
@ -55,19 +55,20 @@ const char *gssapi_users_query =
/** CREATE TABLE statement for the in-memory table */
const char create_sql[] =
"CREATE TABLE IF NOT EXISTS " GSSAPI_TABLE_NAME
"(user varchar(255), host varchar(255), db varchar(255), anydb boolean)";
"(user varchar(255), host varchar(255), db varchar(255), anydb boolean, princ text)";
/** The query that is executed when a user is authenticated */
static const char gssapi_auth_query[] =
"SELECT * FROM " GSSAPI_TABLE_NAME
" WHERE user = '%s' AND '%s' LIKE host AND (anydb = '1' OR '%s' = '' OR '%s' LIKE db) LIMIT 1";
" WHERE user = '%s' AND '%s' LIKE host AND (anydb = '1' OR '%s' = '' OR '%s' LIKE db)"
" AND ('%s' = '%s' OR princ = '%s') LIMIT 1";
/** Delete query used to clean up the database before loading new users */
static const char delete_query[] = "DELETE FROM " GSSAPI_TABLE_NAME;
/** The insert query template which adds users to the gssapi_users table */
static const char insert_sql_pattern[] =
"INSERT INTO " GSSAPI_TABLE_NAME " VALUES ('%s', '%s', %s, %s)";
"INSERT INTO " GSSAPI_TABLE_NAME " VALUES ('%s', '%s', %s, %s, %s)";
/** Used for NULL value creation in the INSERT query */
static const char null_token[] = "NULL";
@ -316,9 +317,10 @@ static gss_name_t server_name = GSS_C_NO_NAME;
*
* @param token Client token
* @param len Length of the token
* @param output Pointer where the client principal name is stored
* @return True if client token is valid
*/
static bool validate_gssapi_token(char* principal, uint8_t* token, size_t len)
static bool validate_gssapi_token(char* principal, uint8_t* token, size_t len, char **output)
{
OM_uint32 major = 0, minor = 0;
gss_buffer_desc server_buf = {0, 0};
@ -350,21 +352,41 @@ static bool validate_gssapi_token(char* principal, uint8_t* token, size_t len)
gss_ctx_id_t handle = NULL;
gss_buffer_desc in = {0, 0};
gss_buffer_desc out = {0, 0};
gss_buffer_desc client_name = {0, 0};
gss_OID_desc *oid;
gss_name_t client;
in.value = token;
in.length = len;
major = gss_accept_sec_context(&minor, &handle, GSS_C_NO_CREDENTIAL,
&in, GSS_C_NO_CHANNEL_BINDINGS,
&server_name, &oid, &out,
&client, &oid, &out,
0, 0, NULL);
if (GSS_ERROR(major))
{
return false;
report_error(major, minor);
return false;
}
major = gss_display_name(&minor, client, &client_name, NULL);
if (GSS_ERROR(major))
{
report_error(major, minor);
return false;
}
char *princ_name = MXS_MALLOC(client_name.length + 1);
if (!princ_name)
{
return false;
}
memcpy(princ_name, (const char*)client_name.value, client_name.length);
princ_name[client_name.length] = '\0';
*output = princ_name;
}
while (major & GSS_S_CONTINUE_NEEDED);
@ -385,17 +407,28 @@ static int auth_cb(void *data, int columns, char** rows, char** row_names)
* @param auth Authenticator session
* @param dcb Client DCB
* @param session MySQL session
* @param princ Client principal name
* @return True if the user has access to the database
*/
static bool validate_user(gssapi_auth_t *auth, DCB *dcb, MYSQL_session *session)
static bool validate_user(gssapi_auth_t *auth, DCB *dcb, MYSQL_session *session, const char *princ)
{
size_t len = sizeof(gssapi_auth_query) + strlen(session->user) +
strlen(session->db) + strlen(dcb->remote);
ss_dassert(princ);
size_t len = sizeof(gssapi_auth_query) + strlen(session->user) * 2 +
strlen(session->db) * 2 + strlen(dcb->remote) + strlen(princ) * 2;
char sql[len + 1];
bool rval = false;
char *err;
sprintf(sql, gssapi_auth_query, session->user, dcb->remote, session->db, session->db);
char princ_user[strlen(princ) + 1];
strcpy(princ_user, princ);
char *at = strchr(princ_user, '@');
if (at)
{
*at = '\0';
}
sprintf(sql, gssapi_auth_query, session->user, dcb->remote, session->db,
session->db, princ_user, session->user, princ);
/**
* Try authentication twice; first time with the current users, second
@ -452,12 +485,15 @@ int gssapi_auth_authenticate(DCB *dcb)
* token that we must validate */
MYSQL_session *ses = (MYSQL_session*)dcb->data;
char *princ = NULL;
if (validate_gssapi_token(instance->principal_name, ses->auth_token, ses->auth_token_len) &&
validate_user(auth, dcb, ses))
if (validate_gssapi_token(instance->principal_name, ses->auth_token, ses->auth_token_len, &princ) &&
validate_user(auth, dcb, ses, princ))
{
rval = MXS_AUTH_SUCCEEDED;
}
MXS_FREE(princ);
}
return rval;
@ -504,7 +540,7 @@ static void delete_old_users(sqlite3 *handle)
* @param anydb Global access to databases
*/
static void add_gssapi_user(sqlite3 *handle, const char *user, const char *host,
const char *db, bool anydb)
const char *db, bool anydb, const char *princ)
{
size_t dblen = db ? strlen(db) + 2 : sizeof(null_token); /** +2 for single quotes */
char dbstr[dblen + 1];
@ -518,9 +554,22 @@ static void add_gssapi_user(sqlite3 *handle, const char *user, const char *host,
strcpy(dbstr, null_token);
}
size_t len = sizeof(insert_sql_pattern) + strlen(user) + strlen(host) + dblen + 1;
size_t princlen = princ && *princ ? strlen(princ) + 2 : sizeof(null_token); /** +2 for single quotes */
char princstr[princlen + 1];
if (princ && *princ)
{
sprintf(princstr, "'%s'", princ);
}
else
{
strcpy(princstr, null_token);
}
size_t len = sizeof(insert_sql_pattern) + strlen(user) + strlen(host) + dblen + princlen + 1;
char insert_sql[len + 1];
sprintf(insert_sql, insert_sql_pattern, user, host, dbstr, anydb ? "1" : "0");
sprintf(insert_sql, insert_sql_pattern, user, host, dbstr, anydb ? "1" : "0", princstr);
char *err;
if (sqlite3_exec(handle, insert_sql, NULL, NULL, &err) != SQLITE_OK)
@ -574,7 +623,8 @@ int gssapi_auth_load_users(SERV_LISTENER *listener)
while ((row = mysql_fetch_row(res)))
{
add_gssapi_user(inst->handle, row[0], row[1], row[2],
row[3] && strcasecmp(row[3], "Y") == 0);
row[3] && strcasecmp(row[3], "Y") == 0,
row[4]);
}
rval = MXS_AUTH_LOADUSERS_OK;