Subject | Fixing a bug - server default to sysdba access |
---|---|
Author | Mark O'Donohue |
Post date | 2000-11-12T17:07:46Z |
Hi
Sorry to bring the tone of conversation down, from the lofty and worthy
talk about torn page routines etc. But if someone would like to comment
on this bug and, probably in general the whole client -> server
connection security I would appreciate it.
There appears to be a hole in super server if you connect without any
user info it defaults you to having the permission of the owner of the
server process (usually root or SYSDBA). This is the one Im looking at.
In addition both super server and classic server accept without question
the text user name, from the trusted client. Trust usually doesn't
extend this far, the name of the client is usually validated by a call
back to the trusted client system on a known port and the identity of
the user is picked up that way. This means (as I read it) that you
trust the system, but you don't have to trust every process on that system.
But back to the first problem, where passing blank credentials results
in getting SYSDBA access.
When connecting remotely to super server if the user does not pass any
logon information across to the server they get connected as the user
under which the server is running (usually sysdba).
In my looking around this seems to boil down to a call to:
SCL_init() - check database access control list (jrd/scl.e).
The function checks a users permission when they attach to a database.
It then calls the function:
ISC_get_user()
Which if we look up the unix definition in jrd/isc.c we find that it
looks through the string passed from the client program looking for a
string of the form:
"<user name>.<gid>.<uid>"
That it accepts this from the client without question is a problem but
that is for another day.
However if the string passed from the client is empty "" then it goes
and defaults to the curret process owner name.
else {
euid = (SLONG) geteuid(); // get current effective uid.
egid = (SLONG) getegid();
If running as super server, this is a mistake, since the server process
is not the user process and is usually the SYSDBA process and often also
root user.
The other process of interest is remote/inet.c/accept_connection which
does the seteruid() call to change the current process user to the one
passed from the client. This is definately called in classic, I fairly
sure it's not called in super (it's not a good call for a threads, but
linux threads are a little different) I haven't looked in too much
detail at classic, but it's possible that it could have a similar
problem, but it depends on getting through the accept_connection withiut
changing the effective user. There are some grey areas there, like when
the user passed has no local setup, or no user is passed, these would
seem, (and I haven't looked in too much detail here) that could end up
potentially with the same problem as super.
Also if running as a direct local connection (gpre and isql and the
like) defaulting to getting the current running user is the correct
thing to do.
So how to fix it. I've looked at a few things, adding a few #ifdef
SUPERSERVER and #ifdef SUPERCLIENT but there is some chatter in the code
(somewhere) about setting a global variable and a function to indicate
if we are running as a server, and that does seem to be the
easiest/nicest solution. So if anyone has any ideas on this I would
appreciate it.
Also any comments about how to inprove the security of the whole client
<-> server thing would be appreciated.
And I am very happy for people to point out where/if I am wrong.
BTW did we get to a proposed solution in the generators discussion, or
is that still ongoing?
Cheers
Mark
Warning code below:
(and more importantly code with tabs!).
ISC_get_user appears in:
jrd/isc.c: - definition of function for each platform.
jrd/isc_cray.c: - didn't bother looking.
jrd/scl.c: - our main man, (the one we're trying to fix.)
wheel = ISC_get_user (name, &id, &group, project, organization,
&node_id, sys_user_name);
Various one to get and extract for the net/remote utilities.
remote/ambx.c:
remote/decnet.c:
remote/dnet.c:
remote/inet.c:
remote/inet.c:
remote/inet_server.c:
remote/ipc.c:
remote/spxnet32.c:
remote/spxwin.c:
remote/wnet.c:
remote/xnet.c:
utilities/guard.c:
Main code of ISC_get_user()
if (user_string && *user_string)
{
for (p = user_name, q = user_string; (*p = *q++) && *p != '.'; p++)
;
*p = 0;
p = user_name;
egid = euid = -1;
if (*q)
{
egid = atoi (q);
while (*q && (*q != '.'))
q++;
if (*q == '.')
{
q++;
euid = atoi (q);
}
}
}
else
{
euid = (SLONG) geteuid();
egid = (SLONG) getegid();
passwd = getpwuid (euid);
if (passwd)
p = passwd->pw_name;
else
p = "";
endpwent();
}
passwd = getpwuid (euid);
if (passwd)
p = passwd->pw_name;
else
p = "";
endpwent();
}
if (name)
strcpy (name, p);
if (id)
*id = euid;
if (group)
*group = egid;
if (project)
*project = 0;
if (organization)
*organization = 0;
if (node)
*node = 0;
return (euid == 0);
}
--
Your database needs YOU!
http://sourceforge.net/projects/firebird
Sorry to bring the tone of conversation down, from the lofty and worthy
talk about torn page routines etc. But if someone would like to comment
on this bug and, probably in general the whole client -> server
connection security I would appreciate it.
There appears to be a hole in super server if you connect without any
user info it defaults you to having the permission of the owner of the
server process (usually root or SYSDBA). This is the one Im looking at.
In addition both super server and classic server accept without question
the text user name, from the trusted client. Trust usually doesn't
extend this far, the name of the client is usually validated by a call
back to the trusted client system on a known port and the identity of
the user is picked up that way. This means (as I read it) that you
trust the system, but you don't have to trust every process on that system.
But back to the first problem, where passing blank credentials results
in getting SYSDBA access.
When connecting remotely to super server if the user does not pass any
logon information across to the server they get connected as the user
under which the server is running (usually sysdba).
In my looking around this seems to boil down to a call to:
SCL_init() - check database access control list (jrd/scl.e).
The function checks a users permission when they attach to a database.
It then calls the function:
ISC_get_user()
Which if we look up the unix definition in jrd/isc.c we find that it
looks through the string passed from the client program looking for a
string of the form:
"<user name>.<gid>.<uid>"
That it accepts this from the client without question is a problem but
that is for another day.
However if the string passed from the client is empty "" then it goes
and defaults to the curret process owner name.
else {
euid = (SLONG) geteuid(); // get current effective uid.
egid = (SLONG) getegid();
If running as super server, this is a mistake, since the server process
is not the user process and is usually the SYSDBA process and often also
root user.
The other process of interest is remote/inet.c/accept_connection which
does the seteruid() call to change the current process user to the one
passed from the client. This is definately called in classic, I fairly
sure it's not called in super (it's not a good call for a threads, but
linux threads are a little different) I haven't looked in too much
detail at classic, but it's possible that it could have a similar
problem, but it depends on getting through the accept_connection withiut
changing the effective user. There are some grey areas there, like when
the user passed has no local setup, or no user is passed, these would
seem, (and I haven't looked in too much detail here) that could end up
potentially with the same problem as super.
Also if running as a direct local connection (gpre and isql and the
like) defaulting to getting the current running user is the correct
thing to do.
So how to fix it. I've looked at a few things, adding a few #ifdef
SUPERSERVER and #ifdef SUPERCLIENT but there is some chatter in the code
(somewhere) about setting a global variable and a function to indicate
if we are running as a server, and that does seem to be the
easiest/nicest solution. So if anyone has any ideas on this I would
appreciate it.
Also any comments about how to inprove the security of the whole client
<-> server thing would be appreciated.
And I am very happy for people to point out where/if I am wrong.
BTW did we get to a proposed solution in the generators discussion, or
is that still ongoing?
Cheers
Mark
Warning code below:
(and more importantly code with tabs!).
ISC_get_user appears in:
jrd/isc.c: - definition of function for each platform.
jrd/isc_cray.c: - didn't bother looking.
jrd/scl.c: - our main man, (the one we're trying to fix.)
wheel = ISC_get_user (name, &id, &group, project, organization,
&node_id, sys_user_name);
Various one to get and extract for the net/remote utilities.
remote/ambx.c:
remote/decnet.c:
remote/dnet.c:
remote/inet.c:
remote/inet.c:
remote/inet_server.c:
remote/ipc.c:
remote/spxnet32.c:
remote/spxwin.c:
remote/wnet.c:
remote/xnet.c:
utilities/guard.c:
Main code of ISC_get_user()
if (user_string && *user_string)
{
for (p = user_name, q = user_string; (*p = *q++) && *p != '.'; p++)
;
*p = 0;
p = user_name;
egid = euid = -1;
if (*q)
{
egid = atoi (q);
while (*q && (*q != '.'))
q++;
if (*q == '.')
{
q++;
euid = atoi (q);
}
}
}
else
{
euid = (SLONG) geteuid();
egid = (SLONG) getegid();
passwd = getpwuid (euid);
if (passwd)
p = passwd->pw_name;
else
p = "";
endpwent();
}
passwd = getpwuid (euid);
if (passwd)
p = passwd->pw_name;
else
p = "";
endpwent();
}
if (name)
strcpy (name, p);
if (id)
*id = euid;
if (group)
*group = egid;
if (project)
*project = 0;
if (organization)
*organization = 0;
if (node)
*node = 0;
return (euid == 0);
}
--
Your database needs YOU!
http://sourceforge.net/projects/firebird