Commit 1d20b232 authored by kostja@oak.local's avatar kostja@oak.local

Bug fixes for authentication

OLD_PASSWORD made a keyword to allow set password=old_password('abc') constructions.
parent 7df04758
......@@ -624,3 +624,8 @@ vio/test-sslserver
vio/viotest-ssl
start_mysqld.sh
mysys/main.cc
BitKeeper/post-commit
BitKeeper/post-commit-manual
build_tags.sh
tests/connect_test
BUILD/compile-pentium-maintainer
......@@ -327,7 +327,7 @@ void get_salt_from_password_323(unsigned long *res, const char *password);
void make_password_from_salt_323(char *to, const unsigned long *salt);
void make_scrambled_password(char *to, const char *password);
char *scramble(char *to, const char *message, const char *password);
void scramble(char *to, const char *message, const char *password);
my_bool check_scramble(const char *reply, const char *message,
const unsigned char *hash_stage2);
void get_salt_from_password(unsigned char *res, const char *password);
......
......@@ -619,16 +619,20 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
/* write scrambled password according to server capabilities */
if (passwd[0])
{
/* Write NULL-terminated scrambled password: */
end= mysql->server_capabilities & CLIENT_SECURE_CONNECTION ?
scramble(end, mysql->scramble, passwd) :
scramble_323(end, mysql->scramble_323, passwd,
(my_bool) (mysql->protocol_version == 9));
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
{
*end++= SCRAMBLE_LENGTH;
scramble(end, mysql->scramble, passwd);
end+= SCRAMBLE_LENGTH;
}
else
end= scramble_323(end, mysql->scramble_323, passwd,
(my_bool) (mysql->protocol_version == 9)) + 1;
}
else
*end= '\0'; // empty password
*end++= '\0'; // empty password
/* Add database if needed */
end=strmov(end+1,db ? db : "");
end= strmov(end, db ? db : "") + 1;
/* Write authentication package */
simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1);
......
......@@ -1823,7 +1823,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
mysql->server_status, client_flag));
/* This needs to be changed as it's not useful with big packets */
if (user && user[0])
strmake(end,user,32); /* Max user name */
strmake(end,user,USERNAME_LENGTH); /* Max user name */
else
read_user_name((char*) end);
......@@ -1835,21 +1835,25 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
end= strend(end) + 1;
if (passwd[0])
{
/* Write NULL-terminated scrambled password: */
end= mysql->server_capabilities & CLIENT_SECURE_CONNECTION ?
scramble(end, mysql->scramble, passwd) :
scramble_323(end, mysql->scramble_323, passwd,
(my_bool) (mysql->protocol_version == 9));
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
{
*end++= SCRAMBLE_LENGTH;
scramble(end, mysql->scramble, passwd);
end+= SCRAMBLE_LENGTH;
}
else
*end= '\0'; /* empty password */
end= scramble_323(end, mysql->scramble_323, passwd,
(my_bool) (mysql->protocol_version == 9)) + 1;
}
else
*end++= '\0'; /* empty password */
/* Add database if needed */
if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
{
end=strmake(end+1,db,NAME_LEN);
mysql->db=my_strdup(db,MYF(MY_WME));
db=0;
end= strmake(end, db, NAME_LEN) + 1;
mysql->db= my_strdup(db,MYF(MY_WME));
db= 0;
}
/* Write authentication package */
if (my_net_write(net,buff,(ulong) (end-buff)) || net_flush(net))
......
......@@ -52,13 +52,6 @@ Item *create_func_ord(Item* a)
return new Item_func_ord(a);
}
Item *create_func_old_password(Item* a)
{
return new Item_func_old_password(a);
}
Item *create_func_asin(Item* a)
{
return new Item_func_asin(a);
......@@ -332,11 +325,6 @@ Item *create_func_quarter(Item* a)
return new Item_func_quarter(a);
}
Item *create_func_password(Item* a)
{
return new Item_func_password(a);
}
Item *create_func_radians(Item *a)
{
return new Item_func_units((char*) "radians",a,M_PI/180,0.0);
......
......@@ -69,14 +69,12 @@ Item *create_func_monthname(Item* a);
Item *create_func_nullif(Item* a, Item *b);
Item *create_func_oct(Item *);
Item *create_func_ord(Item* a);
Item *create_func_old_password(Item* a);
Item *create_func_period_add(Item* a, Item *b);
Item *create_func_period_diff(Item* a, Item *b);
Item *create_func_pi(void);
Item *create_func_pow(Item* a, Item *b);
Item *create_func_current_user(void);
Item *create_func_quarter(Item* a);
Item *create_func_password(Item* a);
Item *create_func_radians(Item *a);
Item *create_func_release_lock(Item* a);
Item *create_func_repeat(Item* a, Item *b);
......
......@@ -1360,6 +1360,14 @@ String *Item_func_password::val_str(String *str)
return str;
}
char *Item_func_password::alloc(THD *thd, const char *password)
{
char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
if (buff)
make_scrambled_password(buff, password);
return buff;
}
/* Item_func_old_password */
String *Item_func_old_password::val_str(String *str)
......@@ -1374,6 +1382,14 @@ String *Item_func_old_password::val_str(String *str)
return str;
}
char *Item_func_old_password::alloc(THD *thd, const char *password)
{
char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
if (buff)
make_scrambled_password_323(buff, password);
return buff;
}
#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
......
......@@ -270,6 +270,7 @@ public:
String *val_str(String *str);
void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; }
const char *func_name() const { return "password"; }
static char *alloc(THD *thd, const char *password);
};
......@@ -288,7 +289,7 @@ public:
String *val_str(String *str);
void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; }
const char *func_name() const { return "old_password"; }
unsigned int size_of() { return sizeof(*this);}
static char *alloc(THD *thd, const char *password);
};
......
......@@ -284,6 +284,7 @@ static SYMBOL symbols[] = {
{ "NULL", SYM(NULL_SYM),0,0},
{ "NUMERIC", SYM(NUMERIC_SYM),0,0},
{ "OFFSET", SYM(OFFSET_SYM),0,0},
{ "OLD_PASSWORD", SYM(OLD_PASSWORD),0,0},
{ "ON", SYM(ON),0,0},
{ "OPEN", SYM(OPEN_SYM),0,0},
{ "OPTIMIZE", SYM(OPTIMIZE),0,0},
......@@ -577,7 +578,6 @@ static SYMBOL sql_functions[] = {
{ "NUMPOINTS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_numpoints)},
{ "OCTET_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_length)},
{ "OCT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_oct)},
{ "OLD_PASSWORD", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_old_password)},
{ "ORD", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ord)},
{ "OVERLAPS", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_overlaps)},
{ "PERIOD_ADD", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_period_add)},
......
......@@ -446,22 +446,20 @@ make_scrambled_password(char *to, const char *password)
Produce an obscure octet sequence from password and random
string, recieved from the server. This sequence corresponds to the
password, but password can not be easily restored from it. The sequence
is then sent to the server for validation. Trailing zero is stored in
the buf.
is then sent to the server for validation. Trailing zero is not stored
in the buf as it is not needed.
This function is used by client to create authenticated reply to the
server's greeting.
SYNOPSIS
scramble()
buf OUT store scrambled string here. The buf must be at least
SHA1_HASH_SIZE+1 bytes long.
SHA1_HASH_SIZE bytes long.
message IN random message, must be exactly SCRAMBLE_LENGTH long and
NULL-terminated.
password IN users' password
RETURN VALUE
end of scrambled string
*/
char *
void
scramble(char *to, const char *message, const char *password)
{
SHA1_CONTEXT sha1_context;
......@@ -483,8 +481,6 @@ scramble(char *to, const char *message, const char *password)
/* xor allows 'from' and 'to' overlap: lets take advantage of it */
sha1_result(&sha1_context, (uint8 *) to);
my_crypt(to, (const uint8 *) to, hash_stage1, SCRAMBLE_LENGTH);
to[SHA1_HASH_SIZE]= '\0';
return to + SHA1_HASH_SIZE;
}
......
......@@ -51,7 +51,7 @@ static byte* acl_entry_get_key(acl_entry *entry,uint *length,
return (byte*) entry->key;
}
#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+17)
#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+USERNAME_LENGTH+1)
static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
static MEM_ROOT mem, memex;
......@@ -208,7 +208,8 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
DBUG_PRINT("info",("user table fields: %d, password length: %d",
table->fields, table->field[2]->field_length));
if (table->field[2]->field_length < 41 && !use_old_passwords)
if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH &&
!use_old_passwords)
{
sql_print_error("mysql.user table is not updated to new password format; "
"Disabling new password usage until "
......@@ -516,6 +517,7 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
RETURN VALUE
0 success: thread data and mqh are updated
1 user not found or authentification failure
2 user found, has long (4.1.1) salt, but passwd is in old (3.23) format.
-1 user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
*/
......@@ -564,6 +566,9 @@ acl_getroot(THD *thd, USER_RESOURCES *mqh,
else if (passwd_len == SCRAMBLE_LENGTH &&
user_i->salt_len == SCRAMBLE_LENGTH_323)
res= -1;
else if (passwd_len == SCRAMBLE_LENGTH_323 &&
user_i->salt_len == SCRAMBLE_LENGTH)
res= 2;
/* linear search complete: */
break;
}
......
......@@ -195,11 +195,8 @@ end:
RETURN VALUE
0 OK; thd->user, thd->master_access, thd->priv_user, thd->db and
thd->db_access are updated; OK is sent to client;
1 access denied or internal error; error is sent to client
Note, that this return semantics differs from check_connection,
which returns -1 if message was already sent.
-1 acl entry for this user contains old scramble, but passwd contains
new one, error is not sent to client
-1 access denied or handshake error; error is sent to client;
>0 error, not sent to client
*/
static int check_user(THD *thd, enum enum_server_command command,
......@@ -208,6 +205,11 @@ static int check_user(THD *thd, enum enum_server_command command,
{
DBUG_ENTER("check_user");
if (passwd_len != 0 &&
passwd_len != SCRAMBLE_LENGTH &&
passwd_len != SCRAMBLE_LENGTH_323)
DBUG_RETURN(ER_HANDSHAKE_ERROR);
/*
Why this is set here? - probably to reset current DB to 'no database
selected' in case of 'change user' failure.
......@@ -215,11 +217,43 @@ static int check_user(THD *thd, enum enum_server_command command,
thd->db= 0;
thd->db_length= 0;
char buff[NAME_LEN + 1]; /* to conditionally save db */
USER_RESOURCES ur;
int res= acl_getroot(thd, &ur, passwd, passwd_len,
protocol_version == 9 ||
!(thd->client_capabilities & CLIENT_LONG_PASSWORD));
if (res == 0 && !(thd->master_access & NO_ACCESS)) // authentification is OK
if (res == -1)
{
/*
This happens when client (new) sends password scrambled with
scramble(), but database holds old value (scrambled with
scramble_323()). Here we please client to send scrambled_password
in old format.
*/
/* save db because network buffer is to hold new packet */
if (db)
{
strmake(buff, db, NAME_LEN);
db= buff;
}
NET *net= &thd->net;
if (my_net_write(net, thd->scramble_323, SCRAMBLE_LENGTH_323 + 1) ||
net_flush(net) ||
my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very
{ // specific packet size
inc_host_errors(&thd->remote.sin_addr);
DBUG_RETURN(ER_HANDSHAKE_ERROR);
}
/* Final attempt to check the user based on reply */
/* So as passwd is short, errcode is always >= 0 */
res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323,
false);
}
/* here res is always >= 0 */
if (res == 0)
{
if (!(thd->master_access & NO_ACCESS)) // authentification is OK
{
DBUG_PRINT("info",
("Capabilities: %d packet_length: %ld Host: '%s' "
......@@ -233,13 +267,13 @@ static int check_user(THD *thd, enum enum_server_command command,
if (check_count)
{
VOID(pthread_mutex_lock(&LOCK_thread_count));
bool count_ok= thread_count < max_connections + delayed_insert_threads ||
thd->master_access & SUPER_ACL;
bool count_ok= thread_count < max_connections + delayed_insert_threads
|| thd->master_access & SUPER_ACL;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
if (!count_ok)
{ // too many connections
send_error(thd, ER_CON_COUNT_ERROR);
DBUG_RETURN(1);
DBUG_RETURN(-1);
}
}
......@@ -269,16 +303,22 @@ static int check_user(THD *thd, enum enum_server_command command,
{
if (thd->user_connect)
decrease_user_connections(thd->user_connect);
DBUG_RETURN(1);
DBUG_RETURN(-1);
}
}
else
send_ok(thd);
thd->password= test(passwd_len); // remember for error messages
/* Ready to handle queries */
DBUG_RETURN(0);
}
}
else if (res != -1) // authentication failure
else if (res == 2) // client gave short hash, server has long hash
{
net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
DBUG_RETURN(-1);
}
net_printf(thd, ER_ACCESS_DENIED_ERROR,
thd->user,
thd->host_or_ip,
......@@ -287,8 +327,7 @@ static int check_user(THD *thd, enum enum_server_command command,
thd->user,
thd->host_or_ip,
passwd_len ? ER(ER_YES) : ER(ER_NO));
}
DBUG_RETURN(res);
DBUG_RETURN(-1);
}
/*
......@@ -491,60 +530,6 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
}
/*
Perform check for scrambled password, re-request scrambled password
from client if necessary. See also help for check_user.
SYNOPSIS
authenticate()
RETURN VALUE
0 success, OK sent to client
-1 error, sent to client
> 0 error, not sent to client
*/
static
int
authenticate(THD *thd, enum enum_server_command command,
const char *passwd, uint passwd_len, const char *db,
bool check_count)
{
if (passwd_len != 0 &&
passwd_len != SCRAMBLE_LENGTH &&
passwd_len != SCRAMBLE_LENGTH_323)
return 1;
int res= check_user(thd, COM_CONNECT, passwd, passwd_len, db, check_count);
if (res < 0)
{
/*
This happens when client (new) sends password scrambled with
scramble(), but database holds old value (scrambled with
scramble_323()). Here we please client to send scrambled_password
in old format.
*/
char buff[NAME_LEN + 1];
/* save db because network buffer is to hold new packet */
if (db)
{
strmake(buff, db, NAME_LEN);
db= buff;
}
NET *net= &thd->net;
if (my_net_write(net, thd->scramble_323, SCRAMBLE_LENGTH_323 + 1) ||
net_flush(net) ||
my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very
{ // specific packet size
inc_host_errors(&thd->remote.sin_addr);
return ER_HANDSHAKE_ERROR;
}
/* Final attempt to check the user based on reply */
/* So as passwd is short, errcode is always sent to user and res >= 0 */
res= check_user(thd, COM_CONNECT, (char *) net->read_pos,
SCRAMBLE_LENGTH_323, db, check_count);
}
return res > 0 ? -1 : 0;
}
/*
Perform handshake, authorize client and update thd ACL variables.
SYNOPSIS
......@@ -643,7 +628,7 @@ check_connection(THD *thd)
/*
Old clients does not understand long scrambles, but can ignore packet
tail: that's why first part of scramble is placed here, and second
tail: that's why first part of the scramble is placed here, and second
part at the end of packet.
*/
end= strmake(end, thd->scramble_323, SCRAMBLE_LENGTH_323) + 1;
......@@ -760,17 +745,23 @@ check_connection(THD *thd)
char *user= end;
char *passwd= strend(user)+1;
uint passwd_len= strlen(passwd);
char *db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
passwd+passwd_len+1 : 0;
char *db= passwd;
/*
Old clients send null-terminated string as password; new clients send
the size (1 byte) + string (not null-terminated). Hence in case of empty
password both send '\0'.
*/
uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
*passwd++ : strlen(passwd);
db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
db + passwd_len + 1 : 0;
if (thd->user)
x_free(thd->user);
thd->user= my_strdup(user, MYF(0));
if (!thd->user)
return(ER_OUT_OF_RESOURCES);
return authenticate(thd, COM_CONNECT, passwd, passwd_len, db, true);
return check_user(thd, COM_CONNECT, passwd, passwd_len, db, true);
}
......@@ -1137,8 +1128,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
statistic_increment(com_other, &LOCK_status);
char *user= (char*) packet;
char *passwd= strend(user)+1;
uint passwd_len= strlen(passwd);
char *db= passwd + passwd_len + 1;
/*
Old clients send null-terminated string ('\0' for empty string) for
password. New clients send the size (1 byte) + string (not null
terminated, so also '\0' for empty string).
*/
char *db= passwd;
uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
*passwd++ : strlen(passwd);
db+= passwd_len + 1;
/* Small check for incomming packet */
if ((uint) ((uchar*) db - net->read_pos) > packet_length)
......@@ -1163,7 +1161,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
}
int res= authenticate(thd, COM_CHANGE_USER, passwd, passwd_len, db, false);
int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, false);
if (res)
{
......
......@@ -493,6 +493,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token MULTIPOINT
%token MULTIPOLYGON
%token NOW_SYM
%token OLD_PASSWORD
%token PASSWORD
%token POINTFROMTEXT
%token POINT_SYM
......@@ -2519,6 +2520,8 @@ simple_expr:
$$= use_old_passwords ? (Item *) new Item_func_old_password($3) :
(Item *) new Item_func_password($3);
}
| OLD_PASSWORD '(' expr ')'
{ $$= new Item_func_old_password($3); }
| POINT_SYM '(' expr ',' expr ')'
{ $$= new Item_func_point($3,$5); }
| POINTFROMTEXT '(' expr ')'
......@@ -4412,6 +4415,7 @@ keyword:
| NO_SYM {}
| NONE_SYM {}
| OFFSET_SYM {}
| OLD_PASSWORD {}
| OPEN_SYM {}
| PACK_KEYS_SYM {}
| PARTIAL {}
......@@ -4603,24 +4607,15 @@ text_or_password:
TEXT_STRING { $$=$1.str;}
| PASSWORD '(' TEXT_STRING ')'
{
if (!$3.length)
$$=$3.str;
else if (use_old_passwords)
{
char *buff= (char *)
YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
if (buff)
make_scrambled_password_323(buff, $3.str);
$$=buff;
$$= $3.length ? use_old_passwords ?
Item_func_old_password::alloc(YYTHD, $3.str) :
Item_func_password::alloc(YYTHD, $3.str) :
$3.str;
}
else
| OLD_PASSWORD '(' TEXT_STRING ')'
{
char *buff= (char *)
YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
if (buff)
make_scrambled_password(buff, $3.str);
$$=buff;
}
$$= $3.length ? Item_func_old_password::alloc(YYTHD, $3.str) :
$3.str;
}
;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment