Use password/group cache functions and avoid stashing a pointer to
the return value of getgrgid(3) or getgrnam(3) which relies on
undefined behavior. When run as non-root, rdistd now uses the group
list from getgroups(2) for group membership checks instead of looking
up the group by name. This lets us make better use of the gid
cache. I've also made the tilde expansion in expand.c use exptilde().
This needs further testing by someone who actually uses rdist.
- todd
Index: usr.bin/rdist/common.c
===================================================================
RCS file: /cvs/src/usr.bin/rdist/common.c,v
retrieving revision 1.38
diff -u -p -u -r1.38 common.c
--- usr.bin/rdist/common.c 9 Sep 2018 13:53:11 -0000 1.38
+++ usr.bin/rdist/common.c 13 Sep 2018 17:35:42 -0000
@@ -56,6 +56,8 @@
char host[HOST_NAME_MAX+1]; /* Name of this host */
uid_t userid = (uid_t)-1; /* User's UID */
gid_t groupid = (gid_t)-1; /* User's GID */
+gid_t *gidset = NULL; /* User's GID list */
+int gidsetlen = 0; /* Number of GIDS in list */
char *homedir = NULL; /* User's $HOME */
char *locuser = NULL; /* Local User's name */
int isserver = FALSE; /* We're the server */
@@ -130,6 +132,12 @@ init(int argc, char **argv, char **envp)
locuser = xstrdup(pw->pw_name);
groupid = pw->pw_gid;
gethostname(host, sizeof(host));
+ gidsetlen = (int)sysconf(_SC_NGROUPS_MAX);
+ gidset = xmalloc(gidsetlen * sizeof(gid_t));
+ if ((gidsetlen = getgroups(gidsetlen, gidset)) == -1) {
+ free(gidset);
+ gidsetlen = 0;
+ }
#if 0
if ((cp = strchr(host, '.')) != NULL)
*cp = CNULL;
@@ -436,7 +444,7 @@ getusername(uid_t uid, char *file, opt_t
{
static char buf[100];
static uid_t lastuid = (uid_t)-1;
- struct passwd *pwd = NULL;
+ const char *name;
/*
* The value of opts may have changed so we always
@@ -448,14 +456,14 @@ getusername(uid_t uid, char *file, opt_t
}
/*
- * Try to avoid getpwuid() call.
+ * Try to avoid passwd lookup.
*/
if (lastuid == uid && buf[0] != '\0' && buf[0] != ':')
return(buf);
lastuid = uid;
- if ((pwd = getpwuid(uid)) == NULL) {
+ if ((name = user_from_uid(uid, 1)) == NULL) {
if (IS_ON(opts, DO_DEFOWNER) && !isserver)
(void) strlcpy(buf, defowner, sizeof(buf));
else {
@@ -464,7 +472,7 @@ getusername(uid_t uid, char *file, opt_t
(void) snprintf(buf, sizeof(buf), ":%u", uid);
}
} else {
- (void) strlcpy(buf, pwd->pw_name, sizeof(buf));
+ (void) strlcpy(buf, name, sizeof(buf));
}
return(buf);
@@ -478,7 +486,7 @@ getgroupname(gid_t gid, char *file, opt_
{
static char buf[100];
static gid_t lastgid = (gid_t)-1;
- struct group *grp = NULL;
+ const char *name;
/*
* The value of opts may have changed so we always
@@ -490,14 +498,14 @@ getgroupname(gid_t gid, char *file, opt_
}
/*
- * Try to avoid getgrgid() call.
+ * Try to avoid group lookup.
*/
if (lastgid == gid && buf[0] != '\0' && buf[0] != ':')
return(buf);
lastgid = gid;
- if ((grp = (struct group *)getgrgid(gid)) == NULL) {
+ if ((name = group_from_gid(gid, 1)) == NULL) {
if (IS_ON(opts, DO_DEFGROUP) && !isserver)
(void) strlcpy(buf, defgroup, sizeof(buf));
else {
@@ -506,7 +514,7 @@ getgroupname(gid_t gid, char *file, opt_
(void) snprintf(buf, sizeof(buf), ":%u", gid);
}
} else
- (void) strlcpy(buf, grp->gr_name, sizeof(buf));
+ (void) strlcpy(buf, name, sizeof(buf));
return(buf);
}
@@ -574,6 +582,8 @@ exptilde(char *ebuf, char *file, size_t
{
struct passwd *pw;
char *pw_dir, *rest;
+ static char lastuser[_PW_NAME_LEN + 1];
+ static char lastdir[PATH_MAX];
size_t len;
if (*file != '~') {
@@ -595,13 +605,17 @@ notilde:
else
rest = NULL;
if (strcmp(locuser, file) != 0) {
- if ((pw = getpwnam(file)) == NULL) {
- error("%s: unknown user name", file);
- if (rest != NULL)
- *rest = '/';
- return(NULL);
+ if (strcmp(lastuser, file) != 0) {
+ if ((pw = getpwnam(file)) == NULL) {
+ error("%s: unknown user name", file);
+ if (rest != NULL)
+ *rest = '/';
+ return(NULL);
+ }
+ strlcpy(lastuser, pw->pw_name,
sizeof(lastuser));
+ strlcpy(lastdir, pw->pw_dir, sizeof(lastdir));
}
- pw_dir = pw->pw_dir;
+ pw_dir = lastdir;
}
if (rest != NULL)
*rest = '/';
Index: usr.bin/rdist/defs.h
===================================================================
RCS file: /cvs/src/usr.bin/rdist/defs.h,v
retrieving revision 1.37
diff -u -p -u -r1.37 defs.h
--- usr.bin/rdist/defs.h 9 Sep 2018 13:53:11 -0000 1.37
+++ usr.bin/rdist/defs.h 13 Sep 2018 17:35:42 -0000
@@ -167,6 +167,8 @@ extern int rem_w; /* Remote file descr
extern int rtimeout; /* Response time out in seconds */
extern uid_t userid; /* User ID of rdist user */
extern gid_t groupid; /* Group ID of rdist user */
+extern gid_t *gidset; /* List of group IDs of rdist user */
+extern int gidsetlen; /* Number of group IDs in list */
extern jmp_buf finish_jmpbuf; /* Setjmp buffer for finish() */
extern char defowner[64]; /* Default owner */
extern char defgroup[64]; /* Default group */
Index: usr.bin/rdist/expand.c
===================================================================
RCS file: /cvs/src/usr.bin/rdist/expand.c,v
retrieving revision 1.16
diff -u -p -u -r1.16 expand.c
--- usr.bin/rdist/expand.c 9 Sep 2018 13:53:11 -0000 1.16
+++ usr.bin/rdist/expand.c 13 Sep 2018 19:21:25 -0000
@@ -52,6 +52,7 @@ char *pathp;
char *lastpathp;
char *tilde; /* "~user" if not expanding tilde, else "" */
char *tpathp;
+char pathbuf[BUFSIZ];
int expany; /* any expansions done? */
char *entp;
@@ -116,7 +117,6 @@ expand(struct namelist *list, int wh) /
{
struct namelist *nl, *prev;
int n;
- char pathbuf[BUFSIZ];
if (debug)
debugmsg(DM_CALL, "expand(%p, %d) start, list = %s",
@@ -260,36 +260,16 @@ expstr(u_char *s)
return;
}
if (*s == '~') {
- struct passwd *pw;
-
- cp = ++s;
- if (*cp == CNULL || *cp == '/') {
+ if ((cp = strchr(s, '/')) == NULL) {
tilde = "~";
- cp1 = (u_char *)homedir;
+ s++;
} else {
- tilde = (char *)(cp1 = ebuf);
- *cp1++ = '~';
- do
- *cp1++ = *cp++;
- while (*cp && *cp != '/');
- *cp1 = CNULL;
- if (strcmp(locuser, (char *)ebuf+1) != 0) {
- if ((pw = getpwnam((char *)ebuf+1)) == NULL) {
- strlcat((char *)ebuf,
- ": unknown user name",
- sizeof(ebuf));
- yyerror((char *)ebuf+1);
- return;
- }
- cp1 = (u_char *)pw->pw_dir;
- } else {
- cp1 = (u_char *)homedir;
- }
+ tilde = memcpy(ebuf, s, (cp - s));
+ ebuf[cp - s] = '\0';
s = cp;
}
- for (cp = (u_char *)path; (*cp++ = *cp1++) != '\0'; )
- continue;
- tpathp = pathp = (char *)cp - 1;
+ cp = exptilde(path, tilde, sizeof(pathbuf));
+ tpathp = pathp = (char *)cp;
} else {
tpathp = pathp = path;
tilde = "";
Index: usr.bin/rdistd/server.c
===================================================================
RCS file: /cvs/src/usr.bin/rdistd/server.c,v
retrieving revision 1.44
diff -u -p -u -r1.44 server.c
--- usr.bin/rdistd/server.c 9 Sep 2018 13:53:11 -0000 1.44
+++ usr.bin/rdistd/server.c 13 Sep 2018 17:35:42 -0000
@@ -177,7 +177,6 @@ setfilemode(char *file, int fd, int mode
static int
fchog(int fd, char *file, char *owner, char *group, int mode)
{
- static struct group *gr = NULL;
int i;
struct stat st;
uid_t uid;
@@ -189,9 +188,7 @@ fchog(int fd, char *file, char *owner, c
if (*owner == ':') {
uid = (uid_t) atoi(owner + 1);
} else if (strcmp(owner, locuser) != 0) {
- struct passwd *pw;
-
- if ((pw = getpwnam(owner)) == NULL) {
+ if (uid_from_user(owner, &uid) == -1) {
if (mode != -1 && IS_ON(mode, S_ISUID)) {
message(MT_NOTICE,
"%s: unknown login name \"%s\", clearing setuid",
@@ -202,8 +199,7 @@ fchog(int fd, char *file, char *owner, c
message(MT_NOTICE,
"%s: unknown login name \"%s\"",
target, owner);
- } else
- uid = pw->pw_uid;
+ }
} else {
uid = userid;
primegid = groupid;
@@ -213,8 +209,6 @@ fchog(int fd, char *file, char *owner, c
goto ok;
}
} else { /* not root, setuid only if user==owner */
- struct passwd *lupw;
-
if (mode != -1) {
if (IS_ON(mode, S_ISUID) &&
strcmp(locuser, owner) != 0)
@@ -222,35 +216,29 @@ fchog(int fd, char *file, char *owner, c
if (mode)
mode &= ~S_ISVTX; /* and strip sticky too */
}
-
- if ((lupw = getpwnam(locuser)) != NULL)
- primegid = lupw->pw_gid;
+ primegid = groupid;
}
gid = (gid_t)-1;
- if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
- if ((*group == ':' &&
- (getgrgid(gid = atoi(group + 1)) == NULL))
- || ((gr = (struct group *)getgrnam(group)) == NULL)) {
- if (mode != -1 && IS_ON(mode, S_ISGID)) {
- message(MT_NOTICE,
- "%s: unknown group \"%s\", clearing setgid",
- target, group);
- mode &= ~S_ISGID;
- } else
- message(MT_NOTICE,
- "%s: unknown group \"%s\"",
- target, group);
+ if (*group == ':') {
+ gid = (gid_t) atoi(group + 1);
+ } else if (gid_from_group(group, &gid) == -1) {
+ if (mode != -1 && IS_ON(mode, S_ISGID)) {
+ message(MT_NOTICE,
+ "%s: unknown group \"%s\", clearing setgid",
+ target, group);
+ mode &= ~S_ISGID;
} else
- gid = gr->gr_gid;
- } else
- gid = gr->gr_gid;
+ message(MT_NOTICE,
+ "%s: unknown group \"%s\"",
+ target, group);
+ }
if (userid && gid != (gid_t)-1 && gid != primegid) {
- if (gr)
- for (i = 0; gr->gr_mem[i] != NULL; i++)
- if (strcmp(locuser, gr->gr_mem[i]) == 0)
- goto ok;
+ for (i = 0; i < gidsetlen; i++) {
+ if (gid == gidset[i])
+ goto ok;
+ }
if (mode != -1 && IS_ON(mode, S_ISGID)) {
message(MT_NOTICE,
"%s: user %s not in group %s, clearing setgid",