diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index 773dd7487389..4860292c1196 100644 --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -8,7 +8,7 @@ SHLIBDIR?= /lib LIB= util SHLIB_MAJOR= 7 -SRCS= _secure_path.c auth.c expand_number.c flopen.c fparseln.c \ +SRCS= _secure_path.c auth.c gr_util.c expand_number.c flopen.c fparseln.c \ humanize_number.c kld.c login.c login_auth.c login_cap.c login_class.c \ login_crypt.c login_ok.c login_times.c login_tty.c logout.c \ logwtmp.c pidfile.c property.c pty.c pw_util.c realhostname.c \ diff --git a/lib/libutil/gr_util.c b/lib/libutil/gr_util.c new file mode 100644 index 000000000000..89f5fa385416 --- /dev/null +++ b/lib/libutil/gr_util.c @@ -0,0 +1,234 @@ +/*- + * Copyright (c) 2008 Sean C. Farley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + + +static const char GroupLineFormat[] = "%s:%s:%ju:"; + + +/* + * Compares two struct group's. + */ +int +gr_equal(const struct group *gr1, const struct group *gr2) +{ + bool found; + bool equal; + int gr1Ndx; + int gr2Ndx; + + /* Check that the non-member information is the same. */ + equal = strcmp(gr1->gr_name, gr2->gr_name) == 0 && + strcmp(gr1->gr_passwd, gr2->gr_passwd) == 0 && + gr1->gr_gid == gr2->gr_gid; + + /* Check all members in both groups. */ + if (equal) { + for (found = false, gr1Ndx = 0; gr1->gr_mem[gr1Ndx] != NULL; + gr1Ndx++) { + for (gr2Ndx = 0; gr2->gr_mem[gr2Ndx] != NULL; gr2Ndx++) + if (strcmp(gr1->gr_mem[gr1Ndx], + gr2->gr_mem[gr2Ndx]) == 0) { + found = true; + break; + } + if (! found) { + equal = false; + break; + } + } + + /* Check that group2 does not have more members than group1. */ + if (gr2->gr_mem[gr1Ndx] != NULL) + equal = false; + } + + return (equal); +} + + +/* + * Make a group line out of a struct group. + */ +char * +gr_make(const struct group *gr) +{ + char *line; + int ndx; + size_t lineSize; + + /* Calculate the length of the group line. */ + lineSize = snprintf(NULL, 0, GroupLineFormat, gr->gr_name, + gr->gr_passwd, (uintmax_t)gr->gr_gid) + 1; + for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) + lineSize += strlen(gr->gr_mem[ndx]) + 1; + if (ndx > 0) + lineSize--; + + /* Create the group line and fill it. */ + if ((line = malloc(lineSize)) == NULL) + return (NULL); + lineSize = snprintf(line, lineSize, GroupLineFormat, gr->gr_name, + gr->gr_passwd, (uintmax_t)gr->gr_gid); + for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) { + strcat(line, gr->gr_mem[ndx]); + if (gr->gr_mem[ndx + 1] != NULL) + strcat(line, ","); + } + + return (line); +} + + +/* + * Duplicate a struct group. + */ +struct group * +gr_dup(const struct group *gr) +{ + int ndx; + int numMem; + size_t len; + struct group *ngr; + + /* Calculate size of group. */ + len = sizeof(*gr) + + (gr->gr_name != NULL ? strlen(gr->gr_name) + 1 : 0) + + (gr->gr_passwd != NULL ? strlen(gr->gr_passwd) + 1 : 0); + numMem = 0; + if (gr->gr_mem != NULL) { + for (; gr->gr_mem[numMem] != NULL; numMem++) + len += strlen(gr->gr_mem[numMem]) + 1; + len += (numMem + 1) * sizeof(*(gr->gr_mem)); + } + + /* Create new group and copy old group into it. */ + if ((ngr = calloc(1, len)) == NULL) + return (NULL); + len = sizeof(*ngr); + ngr->gr_gid = gr->gr_gid; + if (gr->gr_name != NULL) { + ngr->gr_name = (char *)ngr + len; + len += sprintf(ngr->gr_name, "%s", gr->gr_name) + 1; + } + if (gr->gr_passwd != NULL) { + ngr->gr_passwd = (char *)ngr + len; + len += sprintf(ngr->gr_passwd, "%s", gr->gr_passwd) + 1; + } + if (gr->gr_mem != NULL) { + ngr->gr_mem = (char **)((char *)ngr + len); + len += (numMem + 1) * sizeof(*(ngr->gr_mem)); + for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) { + ngr->gr_mem[ndx] = (char *)ngr + len; + len += sprintf(ngr->gr_mem[ndx], "%s", + gr->gr_mem[ndx]) + 1; + } + ngr->gr_mem[ndx] = NULL; + } + + return (ngr); +} + + +/* + * Scan a line and place it into a group structure. + */ +static bool +__gr_scan(char *line, struct group *gr) +{ + char *loc; + int ndx; + + /* Assign non-member information to structure. */ + gr->gr_name = line; + if ((loc = strchr(line, ':')) == NULL) + return (false); + *loc = '\0'; + gr->gr_passwd = loc + 1; + if (*(gr->gr_passwd) == ':') + *(gr->gr_passwd) = '\0'; + else { + if ((loc = strchr(loc + 1, ':')) == NULL) + return (false); + *loc = '\0'; + } + if (sscanf(loc + 1, "%u", &(gr->gr_gid)) != 1) + return (false); + + /* Assign member information to structure. */ + if ((loc = strchr(loc + 1, ':')) == NULL) + return (false); + line = loc + 1; + gr->gr_mem = NULL; + if (*line != '\0') { + ndx = 0; + do { + if ((gr->gr_mem = reallocf(gr->gr_mem, + sizeof(*(gr->gr_mem)) * (ndx + 1))) == NULL) + return (false); + gr->gr_mem[ndx] = strsep(&line, ","); + } while (gr->gr_mem[ndx++] != NULL); + } + + return (true); +} + + +/* + * Create a struct group from a line. + */ +struct group * +gr_scan(const char *line) +{ + char *lineCopy; + struct group *newGr; + struct group gr; + + if ((lineCopy = strdup(line)) == NULL) + return (NULL); + if (!__gr_scan(lineCopy, &gr)) { + free(lineCopy); + return (NULL); + } + newGr = gr_dup(&gr); + free(lineCopy); + if (gr.gr_mem != NULL) + free(gr.gr_mem); + + return (newGr); +} diff --git a/lib/libutil/libutil.h b/lib/libutil/libutil.h index aa663a72c440..8fafe9bbafcb 100644 --- a/lib/libutil/libutil.h +++ b/lib/libutil/libutil.h @@ -119,6 +119,13 @@ const char *pw_tempname(void); int pw_tmp(int _mfd); #endif +#ifdef _GRP_H_ +int gr_equal(const struct group *gr1, const struct group *gr2); +char *gr_make(const struct group *gr); +struct group *gr_dup(const struct group *gr); +struct group *gr_scan(const char *line); +#endif + #ifdef _SYS_PARAM_H_ struct pidfh *pidfile_open(const char *path, mode_t mode, pid_t *pidptr); int pidfile_write(struct pidfh *pfh); diff --git a/tools/regression/lib/libutil/Makefile b/tools/regression/lib/libutil/Makefile index 653bb3fc2170..74972fde555c 100644 --- a/tools/regression/lib/libutil/Makefile +++ b/tools/regression/lib/libutil/Makefile @@ -1,6 +1,6 @@ # $FreeBSD$ -TESTS= test-trimdomain test-trimdomain-nodomain test-flopen +TESTS= test-trimdomain test-trimdomain-nodomain test-flopen test-grp CFLAGS+= -g -Wall -lutil .PHONY: tests diff --git a/tools/regression/lib/libutil/test-grp.c b/tools/regression/lib/libutil/test-grp.c new file mode 100644 index 000000000000..722a0b8a2202 --- /dev/null +++ b/tools/regression/lib/libutil/test-grp.c @@ -0,0 +1,117 @@ +/*- + * Copyright (c) 2008 Sean C. Farley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include + + +/* + * Static values for building and testing an artificial group. + */ +static char grpName[] = "groupName"; +static char grpPasswd[] = "groupPwd"; +static gid_t grpGID = 1234; +static char *grpMems[] = { "mem1", "mem2", "mem3", NULL }; +static const char *origStrGrp = "groupName:groupPwd:1234:mem1,mem2,mem3"; + + +/* + * Build a group to test against without depending on a real group to be found + * within /etc/group. + */ +static void +build_grp(struct group *grp) +{ + grp->gr_name = grpName; + grp->gr_passwd = grpPasswd; + grp->gr_gid = grpGID; + grp->gr_mem = grpMems; + + return; +} + + +int +main(int argc, char **argv) +{ + char *strGrp; + int testNdx; + struct group *dupGrp; + struct group *scanGrp; + struct group origGrp; + + /* Setup. */ + printf("1..4\n"); + testNdx = 0; + + /* Manually build a group using static values. */ + build_grp(&origGrp); + + /* Copy the group. */ + testNdx++; + if ((dupGrp = gr_dup(&origGrp)) == NULL) + printf("not "); + printf("ok %d - %s\n", testNdx, "gr_dup"); + + /* Compare the original and duplicate groups. */ + testNdx++; + if (! gr_equal(&origGrp, dupGrp)) + printf("not "); + printf("ok %d - %s\n", testNdx, "gr_equal"); + + /* Create group string from the duplicate group structure. */ + testNdx++; + strGrp = gr_make(dupGrp); + if (strcmp(strGrp, origStrGrp) != 0) + printf("not "); + printf("ok %d - %s\n", testNdx, "gr_make"); + + /* + * Create group structure from string and compare it to the original + * group structure. + */ + testNdx++; + if ((scanGrp = gr_scan(strGrp)) == NULL || ! gr_equal(&origGrp, + scanGrp)) + printf("not "); + printf("ok %d - %s\n", testNdx, "gr_scan"); + + /* Clean up. */ + free(scanGrp); + free(strGrp); + free(dupGrp); + + exit(EXIT_SUCCESS); +} diff --git a/tools/regression/lib/libutil/test-grp.t b/tools/regression/lib/libutil/test-grp.t new file mode 100644 index 000000000000..cbb7c8cfed26 --- /dev/null +++ b/tools/regression/lib/libutil/test-grp.t @@ -0,0 +1,12 @@ +#!/bin/sh +# +# $FreeBSD$ +# + +base=$(realpath $(dirname $0)) +name=$(basename $0 .t) + +set -e +cd $base +make -s $name >/dev/null +exec $base/$name