Date: Sat, 2 Jun 2012 15:14:13 +0000 (UTC) From: Baptiste Daroussin <bapt@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org Subject: svn commit: r236452 - stable/8/lib/libutil Message-ID: <201206021514.q52FEDgM096325@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: bapt Date: Sat Jun 2 15:14:12 2012 New Revision: 236452 URL: http://svn.freebsd.org/changeset/base/236452 Log: MFC: 228545,229572 Modify pw_copy: - if pw is NULL and oldpw is not NULL then the oldpw is deleted - if pw->pw_name != oldpw->pw_name but pw->pw_uid == oldpw->pw_uid then it renames the user add new gr_* functions so now gr_util API is similar to pw_util API, this allow to manipulate groups in a safe way. Add new pw_make_v7 to make a passwd line (in v7 format) out of a struct passwd Approved by: des (mentor) Modified: stable/8/lib/libutil/gr_util.c stable/8/lib/libutil/libutil.h stable/8/lib/libutil/pw_util.c Directory Properties: stable/8/lib/libutil/ (props changed) Modified: stable/8/lib/libutil/gr_util.c ============================================================================== --- stable/8/lib/libutil/gr_util.c Sat Jun 2 15:13:28 2012 (r236451) +++ stable/8/lib/libutil/gr_util.c Sat Jun 2 15:14:12 2012 (r236452) @@ -28,23 +28,319 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/errno.h> +#include <sys/stat.h> +#include <ctype.h> +#include <err.h> +#include <fcntl.h> #include <grp.h> #include <inttypes.h> #include <libutil.h> +#include <paths.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> struct group_storage { struct group gr; char *members[]; }; +static int lockfd = -1; +static char group_dir[PATH_MAX]; +static char group_file[PATH_MAX]; +static char tempname[PATH_MAX]; +static int initialized; + static const char group_line_format[] = "%s:%s:%ju:"; /* + * Initialize statics + */ +int +gr_init(const char *dir, const char *group) +{ + if (dir == NULL) { + strcpy(group_dir, _PATH_ETC); + } else { + if (strlen(dir) >= sizeof(group_dir)) { + errno = ENAMETOOLONG; + return (-1); + } + strcpy(group_dir, dir); + } + + if (group == NULL) { + if (dir == NULL) { + strcpy(group_file, _PATH_GROUP); + } else if (snprintf(group_file, sizeof(group_file), "%s/group", + group_dir) > (int)sizeof(group_file)) { + errno = ENAMETOOLONG; + return (-1); + } + } else { + if (strlen(group) >= sizeof(group_file)) { + errno = ENAMETOOLONG; + return (-1); + } + strcpy(group_file, group); + } + initialized = 1; + return (0); +} + +/* + * Lock the group file + */ +int +gr_lock(void) +{ + if (*group_file == '\0') + return (-1); + + for (;;) { + struct stat st; + + lockfd = open(group_file, O_RDONLY, 0); + if (lockfd < 0 || fcntl(lockfd, F_SETFD, 1) == -1) + err(1, "%s", group_file); + if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { + if (errno == EWOULDBLOCK) { + errx(1, "the group file is busy"); + } else { + err(1, "could not lock the group file: "); + } + } + if (fstat(lockfd, &st) == -1) + err(1, "fstat() failed: "); + if (st.st_nlink != 0) + break; + close(lockfd); + lockfd = -1; + } + return (lockfd); +} + +/* + * Create and open a presmuably safe temp file for editing group data + */ +int +gr_tmp(int mfd) +{ + char buf[8192]; + ssize_t nr; + const char *p; + int tfd; + + if (*group_file == '\0') + return (-1); + if ((p = strrchr(group_file, '/'))) + ++p; + else + p = group_file; + if (snprintf(tempname, sizeof(tempname), "%.*sgroup.XXXXXX", + (int)(p - group_file), group_file) >= (int)sizeof(tempname)) { + errno = ENAMETOOLONG; + return (-1); + } + if ((tfd = mkstemp(tempname)) == -1) + return (-1); + if (mfd != -1) { + while ((nr = read(mfd, buf, sizeof(buf))) > 0) + if (write(tfd, buf, (size_t)nr) != nr) + break; + if (nr != 0) { + unlink(tempname); + *tempname = '\0'; + close(tfd); + return (-1); + } + } + return (tfd); +} + +/* + * Copy the group file from one descriptor to another, replacing, deleting + * or adding a single record on the way. + */ +int +gr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) +{ + char buf[8192], *end, *line, *p, *q, *r, t; + struct group *fgr; + const struct group *sgr; + size_t len; + int eof, readlen; + + sgr = gr; + if (gr == NULL) { + line = NULL; + if (old_gr == NULL) + return (-1); + sgr = old_gr; + } else if ((line = gr_make(gr)) == NULL) + return (-1); + + eof = 0; + len = 0; + p = q = end = buf; + for (;;) { + /* find the end of the current line */ + for (p = q; q < end && *q != '\0'; ++q) + if (*q == '\n') + break; + + /* if we don't have a complete line, fill up the buffer */ + if (q >= end) { + if (eof) + break; + if ((size_t)(q - p) >= sizeof(buf)) { + warnx("group line too long"); + errno = EINVAL; /* hack */ + goto err; + } + if (p < end) { + q = memmove(buf, p, end -p); + end -= p - buf; + } else { + p = q = end = buf; + } + readlen = read(ffd, end, sizeof(buf) - (end -buf)); + if (readlen == -1) + goto err; + else + len = (size_t)readlen; + if (len == 0 && p == buf) + break; + end += len; + len = end - buf; + if (len < (ssize_t)sizeof(buf)) { + eof = 1; + if (len > 0 && buf[len -1] != '\n') + ++len, *end++ = '\n'; + } + continue; + } + + /* is it a blank line or a comment? */ + for (r = p; r < q && isspace(*r); ++r) + /* nothing */; + if (r == q || *r == '#') { + /* yep */ + if (write(tfd, p, q -p + 1) != q - p + 1) + goto err; + ++q; + continue; + } + + /* is it the one we're looking for? */ + + t = *q; + *q = '\0'; + + fgr = gr_scan(r); + + /* fgr is either a struct group for the current line, + * or NULL if the line is malformed. + */ + + *q = t; + if (fgr == NULL || fgr->gr_gid != sgr->gr_gid) { + /* nope */ + if (fgr != NULL) + free(fgr); + if (write(tfd, p, q - p + 1) != q - p + 1) + goto err; + ++q; + continue; + } + if (old_gr && !gr_equal(fgr, old_gr)) { + warnx("entry inconsistent"); + free(fgr); + errno = EINVAL; /* hack */ + goto err; + } + free(fgr); + + /* it is, replace or remove it */ + if (line != NULL) { + len = strlen(line); + if (write(tfd, line, len) != (int) len) + goto err; + } else { + /* when removed, avoid the \n */ + q++; + } + /* we're done, just copy the rest over */ + for (;;) { + if (write(tfd, q, end - q) != end - q) + goto err; + q = buf; + readlen = read(ffd, buf, sizeof(buf)); + if (readlen == 0) + break; + else + len = (size_t)readlen; + if (readlen == -1) + goto err; + end = buf + len; + } + goto done; + } + + /* if we got here, we didn't find the old entry */ + if (line == NULL) { + errno = ENOENT; + goto err; + } + len = strlen(line); + if ((size_t)write(tfd, line, len) != len || + write(tfd, "\n", 1) != 1) + goto err; + done: + if (line != NULL) + free(line); + return (0); + err: + if (line != NULL) + free(line); + return (-1); +} + +/* + * Regenerate the group file + */ +int +gr_mkdb(void) +{ + return (rename(tempname, group_file)); +} + +/* + * Clean up. Preserver errno for the caller's convenience. + */ +void +gr_fini(void) +{ + int serrno; + + if (!initialized) + return; + initialized = 0; + serrno = errno; + if (*tempname != '\0') { + unlink(tempname); + *tempname = '\0'; + } + if (lockfd != -1) + close(lockfd); + errno = serrno; +} + +/* * Compares two struct group's. */ int Modified: stable/8/lib/libutil/libutil.h ============================================================================== --- stable/8/lib/libutil/libutil.h Sat Jun 2 15:13:28 2012 (r236451) +++ stable/8/lib/libutil/libutil.h Sat Jun 2 15:14:12 2012 (r236452) @@ -152,6 +152,7 @@ int pw_equal(const struct passwd *_pw1, void pw_fini(void); int pw_init(const char *_dir, const char *_master); char *pw_make(const struct passwd *_pw); +char *pw_make_v7(const struct passwd *_pw); int pw_mkdb(const char *_user); int pw_lock(void); struct passwd *pw_scan(const char *_line, int _flags); @@ -160,9 +161,15 @@ int pw_tmp(int _mfd); #endif #ifdef _GRP_H_ +int gr_copy(int __ffd, int _tfd, const struct group *_gr, struct group *_old_gr); +struct group *gr_dup(const struct group *gr); int gr_equal(const struct group *gr1, const struct group *gr2); +void gr_fini(void); +int gr_init(const char *_dir, const char *_master); +int gr_lock(void); char *gr_make(const struct group *gr); -struct group *gr_dup(const struct group *gr); +int gr_mkdb(void); +int gr_tmp(int _mdf); struct group *gr_scan(const char *line); #endif Modified: stable/8/lib/libutil/pw_util.c ============================================================================== --- stable/8/lib/libutil/pw_util.c Sat Jun 2 15:13:28 2012 (r236451) +++ stable/8/lib/libutil/pw_util.c Sat Jun 2 15:14:12 2012 (r236452) @@ -406,22 +406,43 @@ pw_make(const struct passwd *pw) pw->pw_passwd, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, pw->pw_class, (uintmax_t)pw->pw_change, (uintmax_t)pw->pw_expire, pw->pw_gecos, pw->pw_dir, pw->pw_shell); - return line; + return (line); } /* - * Copy password file from one descriptor to another, replacing or adding - * a single record on the way. + * Make a passwd line (in v7 format) out of a struct passwd + */ +char * +pw_make_v7(const struct passwd *pw) +{ + char *line; + + asprintf(&line, "%s:*:%ju:%ju:%s:%s:%s", pw->pw_name, + (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, + pw->pw_gecos, pw->pw_dir, pw->pw_shell); + return (line); +} + +/* + * Copy password file from one descriptor to another, replacing, deleting + * or adding a single record on the way. */ int pw_copy(int ffd, int tfd, const struct passwd *pw, struct passwd *old_pw) { char buf[8192], *end, *line, *p, *q, *r, t; struct passwd *fpw; + const struct passwd *spw; size_t len; int eof, readlen; - if ((line = pw_make(pw)) == NULL) + spw = pw; + if (pw == NULL) { + line = NULL; + if (old_pw == NULL) + return (-1); + spw = old_pw; + } else if ((line = pw_make(pw)) == NULL) return (-1); eof = 0; @@ -489,7 +510,7 @@ pw_copy(int ffd, int tfd, const struct p */ *q = t; - if (fpw == NULL || strcmp(fpw->pw_name, pw->pw_name) != 0) { + if (fpw == NULL || fpw->pw_uid != spw->pw_uid) { /* nope */ if (fpw != NULL) free(fpw); @@ -506,11 +527,15 @@ pw_copy(int ffd, int tfd, const struct p } free(fpw); - /* it is, replace it */ - len = strlen(line); - if (write(tfd, line, len) != (int)len) - goto err; - + /* it is, replace or remove it */ + if (line != NULL) { + len = strlen(line); + if (write(tfd, line, len) != (int)len) + goto err; + } else { + /* when removed, avoid the \n */ + q++; + } /* we're done, just copy the rest over */ for (;;) { if (write(tfd, q, end - q) != end - q) @@ -528,16 +553,22 @@ pw_copy(int ffd, int tfd, const struct p goto done; } - /* if we got here, we have a new entry */ + /* if we got here, we didn't find the old entry */ + if (line == NULL) { + errno = ENOENT; + goto err; + } len = strlen(line); if ((size_t)write(tfd, line, len) != len || write(tfd, "\n", 1) != 1) goto err; done: - free(line); + if (line != NULL) + free(line); return (0); err: - free(line); + if (line != NULL) + free(line); return (-1); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201206021514.q52FEDgM096325>