system.c bug / possible fix
Elrond
elrond at samba-tng.org
Sat Apr 28 16:56:12 GMT 2001
Hi everybody,
system.c (the current version in head 1.66, the version in
2.2.0 release, previous version in TNG) has a flaw in the
getpw*-cache code.
The general problem was found by Mirko Manea
<mami at arena.sci.univr.it>, who found that using another
version fixed problems he had with LDAP and nt home
directories.
After reading the interesting code many times, I found the
bug:
The current code made a copy of the struct passwd returned
by getpw*(), and since libc might overwrite strings, that
were used in that struct, pw_name and pw_eturned to allow the caller to modify. When were copied
into local memory too (and to allow overwriting those
strings).
The problem is now, that pass->pw_dir (the home dir),
wasn't copied, but left as pointer into libc-memory. When
libc was doing a new getpw*, the memory was probably
overwritten.
You might ask, why THIS error didn't appear in the first
cache-version. Because in that version, a pointer to the
passwd-struct in libc was saved. When libc has read a new
entry into that memory, the memory ALSO contained a new uid
and pw_name, which the cache-compare code used and either
got the right values or a "cache miss".
This bug is somewhat related to the bug fixed in the last
commit in HEAD:
"
lib/system.c (Finally) fixed all insure errors in password caching code. We can't
stop libc routines from calling getpwXXX functions, so caching a pointer to them
is impossible. This new code now makes two copies of the returned struct passwd
struct - one used as a cache, one returned to allow the caller to modify. When
doing a lookup we compare against the cached copy. Code is now easier to understand
also.
"
I have appended a patch (for samba.org, TNG has this
already in cvs) for this problem.
I haven't fully tested this patch, but at least my PDC
works fine with it (it worked before, so I don't know, if
it fixes the actual problem.)
Elrond
Index: lib/system.c
===================================================================
RCS file: /work/cvs/tng/source/lib/system.c,v
retrieving revision 1.6
diff -u -p -r1.6 system.c
--- lib/system.c 2001/04/21 14:10:53 1.6
+++ lib/system.c 2001/04/28 09:24:28
@@ -21,7 +21,6 @@
#include "includes.h"
-extern int DEBUGLEVEL;
/*
The idea is that this file will eventually have wrappers around all
@@ -574,6 +573,10 @@ int sys_setgroups(int setlen, gid_t *gid
/*
* We only wrap pw_name and pw_passwd for now as these
* are the only potentially modified fields.
+ * We need to wrap pw_gecos and pw_dir too, because we
+ * otherwise keep pointers to old strings in libc.
+ * pw_shell should be wrapped for the same reason, but it
+ * isn't used by anyone yet.
*/
/**************************************************************************
@@ -583,9 +586,39 @@ int sys_setgroups(int setlen, gid_t *gid
struct saved_pw {
fstring pw_name;
fstring pw_passwd;
+ fstring pw_gecos;
+ fstring pw_dir;
struct passwd pass;
};
+/*
+ * when "pass" in a saved_pw is copied over, it already
+ * contains all pointers, so we can copy the strings over
+ * from there.
+ */
+static void fill_saved_pw(struct saved_pw *pw)
+{
+ struct passwd *pass = &pw->pass;
+
+ if (pass->pw_name) {
+ fstrcpy(pw->pw_name, pass->pw_name);
+ pass->pw_name = pw->pw_name;
+ }
+ if (pass->pw_passwd) {
+ fstrcpy(pw->pw_passwd, pass->pw_passwd);
+ pass->pw_passwd = pw->pw_passwd;
+ }
+ if (pass->pw_gecos) {
+ fstrcpy(pw->pw_gecos, pass->pw_gecos);
+ pass->pw_gecos = pw->pw_gecos;
+ }
+ if (pass->pw_dir) {
+ fstrcpy(pw->pw_dir, pass->pw_dir);
+ pass->pw_dir = pw->pw_dir;
+ }
+}
+
+
static struct saved_pw pw_mod; /* This is the structure returned - can be modified. */
static struct saved_pw pw_cache; /* This is the structure saved - used to check cache. */
@@ -598,30 +631,24 @@ static struct passwd *setup_pwret(struct
{
if (pass == NULL) {
/* Clear the caches. */
- memset(&pw_cache, '\0', sizeof(struct saved_pw));
- memset(&pw_mod, '\0', sizeof(struct saved_pw));
+ ZERO_STRUCT(pw_cache);
+ ZERO_STRUCT(pw_mod);
num_lookups = 0;
return NULL;
}
-
- /* this gets the uid, gid and null pointers */
- memcpy((char *)&pw_mod.pass, pass, sizeof(struct passwd));
- fstrcpy(pw_mod.pw_name, pass->pw_name);
- pw_mod.pass.pw_name = pw_mod.pw_name;
- fstrcpy(pw_mod.pw_passwd, pass->pw_passwd);
- pw_mod.pass.pw_passwd = pw_mod.pw_passwd;
+ /* this gets the uid, gid and pointers */
+ pw_mod.pass = *pass;
+ /* this copies the strings */
+ fill_saved_pw(&pw_mod);
if (pass != &pw_cache.pass) {
/* If it's a cache miss we must also refill the cache. */
- memcpy((char *)&pw_cache.pass, pass, sizeof(struct passwd));
- fstrcpy(pw_cache.pw_name, pass->pw_name);
- pw_cache.pass.pw_name = pw_cache.pw_name;
- fstrcpy(pw_cache.pw_passwd, pass->pw_passwd);
- pw_cache.pass.pw_passwd = pw_cache.pw_passwd;
+ pw_cache.pass = *pass;
+ fill_saved_pw(&pw_cache);
num_lookups = 1;
More information about the samba-technical
mailing list