1*0a6a1f1dSLionel Sambuc /* $NetBSD: grutil.c,v 1.4 2014/06/23 06:57:31 shm Exp $ */
25c007436SBen Gras
35c007436SBen Gras /*-
45c007436SBen Gras * Copyright (c) 2007 The NetBSD Foundation, Inc.
55c007436SBen Gras * All rights reserved.
65c007436SBen Gras *
75c007436SBen Gras * This code is derived from software contributed to The NetBSD Foundation
85c007436SBen Gras * by Brian Ginsbach.
95c007436SBen Gras *
105c007436SBen Gras * Redistribution and use in source and binary forms, with or without
115c007436SBen Gras * modification, are permitted provided that the following conditions
125c007436SBen Gras * are met:
135c007436SBen Gras * 1. Redistributions of source code must retain the above copyright
145c007436SBen Gras * notice, this list of conditions and the following disclaimer.
155c007436SBen Gras * 2. Redistributions in binary form must reproduce the above copyright
165c007436SBen Gras * notice, this list of conditions and the following disclaimer in the
175c007436SBen Gras * documentation and/or other materials provided with the distribution.
185c007436SBen Gras *
195c007436SBen Gras * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
205c007436SBen Gras * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
215c007436SBen Gras * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
225c007436SBen Gras * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
235c007436SBen Gras * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
245c007436SBen Gras * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
255c007436SBen Gras * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
265c007436SBen Gras * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
275c007436SBen Gras * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
285c007436SBen Gras * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
295c007436SBen Gras * POSSIBILITY OF SUCH DAMAGE.
305c007436SBen Gras */
315c007436SBen Gras #include <sys/cdefs.h>
32*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: grutil.c,v 1.4 2014/06/23 06:57:31 shm Exp $");
335c007436SBen Gras
345c007436SBen Gras #include <sys/param.h>
355c007436SBen Gras #include <err.h>
365c007436SBen Gras #include <errno.h>
375c007436SBen Gras #include <grp.h>
385c007436SBen Gras #include <pwd.h>
395c007436SBen Gras #include <stdio.h>
405c007436SBen Gras #include <stdlib.h>
415c007436SBen Gras #include <string.h>
425c007436SBen Gras #include <unistd.h>
435c007436SBen Gras #include <util.h>
445c007436SBen Gras
455c007436SBen Gras #ifdef LOGIN_CAP
465c007436SBen Gras #include <login_cap.h>
475c007436SBen Gras #endif
485c007436SBen Gras
495c007436SBen Gras #include "grutil.h"
505c007436SBen Gras
515c007436SBen Gras typedef enum {
525c007436SBen Gras ADDGRP_NOERROR = 0, /* must be zero */
535c007436SBen Gras ADDGRP_EMALLOC = 1,
545c007436SBen Gras ADDGRP_EGETGROUPS = 2,
555c007436SBen Gras ADDGRP_ESETGROUPS = 3
565c007436SBen Gras } addgrp_ret_t;
575c007436SBen Gras
585c007436SBen Gras static void
free_groups(void * groups)595c007436SBen Gras free_groups(void *groups)
605c007436SBen Gras {
615c007436SBen Gras int oerrno;
625c007436SBen Gras
635c007436SBen Gras oerrno = errno;
645c007436SBen Gras free(groups);
655c007436SBen Gras errno = oerrno;
665c007436SBen Gras }
675c007436SBen Gras
685c007436SBen Gras static addgrp_ret_t
alloc_groups(int * ngroups,gid_t ** groups,int * ngroupsmax)695c007436SBen Gras alloc_groups(int *ngroups, gid_t **groups, int *ngroupsmax)
705c007436SBen Gras {
715c007436SBen Gras *ngroupsmax = (int)sysconf(_SC_NGROUPS_MAX);
725c007436SBen Gras if (*ngroupsmax < 0)
735c007436SBen Gras *ngroupsmax = NGROUPS_MAX;
745c007436SBen Gras
755c007436SBen Gras *groups = malloc(*ngroupsmax * sizeof(**groups));
765c007436SBen Gras if (*groups == NULL)
775c007436SBen Gras return ADDGRP_EMALLOC;
785c007436SBen Gras
795c007436SBen Gras *ngroups = getgroups(*ngroupsmax, *groups);
805c007436SBen Gras if (*ngroups == -1) {
815c007436SBen Gras free_groups(*groups);
825c007436SBen Gras return ADDGRP_ESETGROUPS;
835c007436SBen Gras }
845c007436SBen Gras return ADDGRP_NOERROR;
855c007436SBen Gras }
865c007436SBen Gras
875c007436SBen Gras static addgrp_ret_t
addgid(gid_t * groups,int ngroups,int ngroupsmax,gid_t gid,int makespace)885c007436SBen Gras addgid(gid_t *groups, int ngroups, int ngroupsmax, gid_t gid, int makespace)
895c007436SBen Gras {
905c007436SBen Gras int i;
915c007436SBen Gras
925c007436SBen Gras /* search for gid in supplemental group list */
935c007436SBen Gras for (i = 0; i < ngroups && groups[i] != gid; i++)
945c007436SBen Gras continue;
955c007436SBen Gras
965c007436SBen Gras /* add the gid to the supplemental group list */
975c007436SBen Gras if (i == ngroups) {
985c007436SBen Gras if (ngroups < ngroupsmax)
995c007436SBen Gras groups[ngroups++] = gid;
1005c007436SBen Gras else { /*
1015c007436SBen Gras * setgroups(2) will fail with errno = EINVAL
1025c007436SBen Gras * if ngroups > nmaxgroups. If makespace is
1035c007436SBen Gras * set, replace the last group with the new
1045c007436SBen Gras * one. Otherwise, fail the way setgroups(2)
1055c007436SBen Gras * would if we passed the larger groups array.
1065c007436SBen Gras */
1075c007436SBen Gras if (makespace) {
1085c007436SBen Gras /*
1095c007436SBen Gras * Find a slot that doesn't contain
1105c007436SBen Gras * the primary group.
1115c007436SBen Gras */
1125c007436SBen Gras struct passwd *pwd;
1135c007436SBen Gras gid_t pgid;
1145c007436SBen Gras pwd = getpwuid(getuid());
1155c007436SBen Gras if (pwd == NULL)
1165c007436SBen Gras goto error;
1175c007436SBen Gras pgid = pwd->pw_gid;
1185c007436SBen Gras for (i = ngroupsmax - 1; i >= 0; i--)
1195c007436SBen Gras if (groups[i] != pgid)
1205c007436SBen Gras break;
1215c007436SBen Gras if (i < 0)
1225c007436SBen Gras goto error;
1235c007436SBen Gras groups[i] = gid;
1245c007436SBen Gras }
1255c007436SBen Gras else {
1265c007436SBen Gras error:
1275c007436SBen Gras errno = EINVAL;
1285c007436SBen Gras return ADDGRP_ESETGROUPS;
1295c007436SBen Gras }
1305c007436SBen Gras }
1315c007436SBen Gras if (setgroups(ngroups, groups) < 0)
1325c007436SBen Gras return ADDGRP_ESETGROUPS;
1335c007436SBen Gras }
1345c007436SBen Gras return ADDGRP_NOERROR;
1355c007436SBen Gras }
1365c007436SBen Gras
1375c007436SBen Gras static addgrp_ret_t
addgrp(gid_t newgid,int makespace)1385c007436SBen Gras addgrp(gid_t newgid, int makespace)
1395c007436SBen Gras {
14084d9c625SLionel Sambuc int ngroups, ngroupsmax;
14184d9c625SLionel Sambuc addgrp_ret_t rval;
1425c007436SBen Gras gid_t *groups;
1435c007436SBen Gras gid_t oldgid;
1445c007436SBen Gras
1455c007436SBen Gras oldgid = getgid();
1465c007436SBen Gras if (oldgid == newgid) /* nothing to do */
1475c007436SBen Gras return ADDGRP_NOERROR;
1485c007436SBen Gras
1495c007436SBen Gras rval = alloc_groups(&ngroups, &groups, &ngroupsmax);
150*0a6a1f1dSLionel Sambuc if (rval != ADDGRP_NOERROR)
1515c007436SBen Gras return rval;
1525c007436SBen Gras
1535c007436SBen Gras /*
1545c007436SBen Gras * BSD based systems normally have the egid in the supplemental
1555c007436SBen Gras * group list.
1565c007436SBen Gras */
1575c007436SBen Gras #if (defined(BSD) && BSD >= 199306)
1585c007436SBen Gras /*
1595c007436SBen Gras * According to POSIX/XPG6:
1605c007436SBen Gras * On system where the egid is normally in the supplemental group list
1615c007436SBen Gras * (or whenever the old egid actually is in the supplemental group
1625c007436SBen Gras * list):
1635c007436SBen Gras * o If the new egid is in the supplemental group list,
1645c007436SBen Gras * just change the egid.
1655c007436SBen Gras * o If the new egid is not in the supplemental group list,
1665c007436SBen Gras * add the new egid to the list if there is room.
1675c007436SBen Gras */
1685c007436SBen Gras
1695c007436SBen Gras rval = addgid(groups, ngroups, ngroupsmax, newgid, makespace);
1705c007436SBen Gras #else
1715c007436SBen Gras /*
1725c007436SBen Gras * According to POSIX/XPG6:
1735c007436SBen Gras * On systems where the egid is not normally in the supplemental group
1745c007436SBen Gras * list (or whenever the old egid is not in the supplemental group
1755c007436SBen Gras * list):
1765c007436SBen Gras * o If the new egid is in the supplemental group list, delete
1775c007436SBen Gras * it from the list.
1785c007436SBen Gras * o If the old egid is not in the supplemental group list,
1795c007436SBen Gras * add the old egid to the list if there is room.
1805c007436SBen Gras */
1815c007436SBen Gras {
1825c007436SBen Gras int i;
1835c007436SBen Gras
1845c007436SBen Gras /* search for new egid in supplemental group list */
1855c007436SBen Gras for (i = 0; i < ngroups && groups[i] != newgid; i++)
1865c007436SBen Gras continue;
1875c007436SBen Gras
1885c007436SBen Gras /* remove new egid from supplemental group list */
1895c007436SBen Gras if (i != ngroups)
1905c007436SBen Gras for (--ngroups; i < ngroups; i++)
1915c007436SBen Gras groups[i] = groups[i + 1];
1925c007436SBen Gras
1935c007436SBen Gras rval = addgid(groups, ngroups, ngroupsmax, oldgid, makespace);
1945c007436SBen Gras }
1955c007436SBen Gras #endif
1965c007436SBen Gras free_groups(groups);
1975c007436SBen Gras return rval;
1985c007436SBen Gras }
1995c007436SBen Gras
2005c007436SBen Gras /*
2015c007436SBen Gras * If newgrp fails, it returns (gid_t)-1 and the errno variable is
2025c007436SBen Gras * set to:
2035c007436SBen Gras * [EINVAL] Unknown group.
2045c007436SBen Gras * [EPERM] Bad password.
2055c007436SBen Gras */
2065c007436SBen Gras static gid_t
newgrp(const char * gname,struct passwd * pwd,uid_t ruid,const char * prompt)2075c007436SBen Gras newgrp(const char *gname, struct passwd *pwd, uid_t ruid, const char *prompt)
2085c007436SBen Gras {
2095c007436SBen Gras struct group *grp;
2105c007436SBen Gras char **ap;
2115c007436SBen Gras char *p;
2125c007436SBen Gras gid_t *groups;
2135c007436SBen Gras int ngroups, ngroupsmax;
2145c007436SBen Gras
2155c007436SBen Gras if (gname == NULL)
2165c007436SBen Gras return pwd->pw_gid;
2175c007436SBen Gras
2185c007436SBen Gras grp = getgrnam(gname);
2195c007436SBen Gras
2205c007436SBen Gras #ifdef GRUTIL_ACCEPT_GROUP_NUMBERS
2215c007436SBen Gras if (grp == NULL) {
2225c007436SBen Gras gid_t gid;
2235c007436SBen Gras if (*gname != '-') {
2245c007436SBen Gras gid = (gid_t)strtol(gname, &p, 10);
2255c007436SBen Gras if (*p == '\0')
2265c007436SBen Gras grp = getgrgid(gid);
2275c007436SBen Gras }
2285c007436SBen Gras }
2295c007436SBen Gras #endif
2305c007436SBen Gras if (grp == NULL) {
2315c007436SBen Gras errno = EINVAL;
2325c007436SBen Gras return (gid_t)-1;
2335c007436SBen Gras }
2345c007436SBen Gras
2355c007436SBen Gras if (ruid == 0 || pwd->pw_gid == grp->gr_gid)
2365c007436SBen Gras return grp->gr_gid;
2375c007436SBen Gras
238*0a6a1f1dSLionel Sambuc if (alloc_groups(&ngroups, &groups, &ngroupsmax) == ADDGRP_NOERROR) {
2395c007436SBen Gras int i;
2405c007436SBen Gras for (i = 0; i < ngroups; i++)
2415c007436SBen Gras if (groups[i] == grp->gr_gid) {
2425c007436SBen Gras free_groups(groups);
2435c007436SBen Gras return grp->gr_gid;
2445c007436SBen Gras }
2455c007436SBen Gras free_groups(groups);
2465c007436SBen Gras }
2475c007436SBen Gras
2485c007436SBen Gras /*
2495c007436SBen Gras * Check the group membership list in case the groups[] array
2505c007436SBen Gras * was maxed out or the user has been added to it since login.
2515c007436SBen Gras */
2525c007436SBen Gras for (ap = grp->gr_mem; *ap != NULL; ap++)
2535c007436SBen Gras if (strcmp(*ap, pwd->pw_name) == 0)
2545c007436SBen Gras return grp->gr_gid;
2555c007436SBen Gras
2565c007436SBen Gras if (*grp->gr_passwd != '\0') {
2575c007436SBen Gras p = getpass(prompt);
2585c007436SBen Gras if (strcmp(grp->gr_passwd, crypt(p, grp->gr_passwd)) == 0) {
2595c007436SBen Gras (void)memset(p, '\0', _PASSWORD_LEN);
2605c007436SBen Gras return grp->gr_gid;
2615c007436SBen Gras }
2625c007436SBen Gras (void)memset(p, '\0', _PASSWORD_LEN);
2635c007436SBen Gras }
2645c007436SBen Gras
2655c007436SBen Gras errno = EPERM;
2665c007436SBen Gras return (gid_t)-1;
2675c007436SBen Gras }
2685c007436SBen Gras
2695c007436SBen Gras #ifdef GRUTIL_SETGROUPS_MAKESPACE
2705c007436SBen Gras # define ADDGRP_MAKESPACE 1
2715c007436SBen Gras #else
2725c007436SBen Gras # define ADDGRP_MAKESPACE 0
2735c007436SBen Gras #endif
2745c007436SBen Gras
2755c007436SBen Gras #ifdef GRUTIL_ALLOW_GROUP_ERRORS
2765c007436SBen Gras # define maybe_exit(e)
2775c007436SBen Gras #else
2785c007436SBen Gras # define maybe_exit(e) exit(e);
2795c007436SBen Gras #endif
2805c007436SBen Gras
2815c007436SBen Gras void
addgroup(login_cap_t * lc,const char * gname,struct passwd * pwd,uid_t ruid,const char * prompt)2825c007436SBen Gras addgroup(
2835c007436SBen Gras #ifdef LOGIN_CAP
2845c007436SBen Gras login_cap_t *lc,
2855c007436SBen Gras #endif
2865c007436SBen Gras const char *gname, struct passwd *pwd, uid_t ruid, const char *prompt)
2875c007436SBen Gras {
2885c007436SBen Gras pwd->pw_gid = newgrp(gname, pwd, ruid, prompt);
2895c007436SBen Gras if (pwd->pw_gid == (gid_t)-1) {
2905c007436SBen Gras switch (errno) {
2915c007436SBen Gras case EINVAL:
2925c007436SBen Gras warnx("Unknown group `%s'", gname);
2935c007436SBen Gras maybe_exit(EXIT_FAILURE);
2945c007436SBen Gras break;
2955c007436SBen Gras case EPERM: /* password failure */
2965c007436SBen Gras warnx("Sorry");
2975c007436SBen Gras maybe_exit(EXIT_FAILURE);
2985c007436SBen Gras break;
2995c007436SBen Gras default: /* XXX - should never happen */
3005c007436SBen Gras err(EXIT_FAILURE, "unknown error");
3015c007436SBen Gras break;
3025c007436SBen Gras }
3035c007436SBen Gras pwd->pw_gid = getgid();
3045c007436SBen Gras }
3055c007436SBen Gras
3065c007436SBen Gras switch (addgrp(pwd->pw_gid, ADDGRP_MAKESPACE)) {
3075c007436SBen Gras case ADDGRP_NOERROR:
3085c007436SBen Gras break;
3095c007436SBen Gras case ADDGRP_EMALLOC:
3105c007436SBen Gras err(EXIT_FAILURE, "malloc");
3115c007436SBen Gras break;
3125c007436SBen Gras case ADDGRP_EGETGROUPS:
3135c007436SBen Gras err(EXIT_FAILURE, "getgroups");
3145c007436SBen Gras break;
3155c007436SBen Gras case ADDGRP_ESETGROUPS:
3165c007436SBen Gras switch(errno) {
3175c007436SBen Gras case EINVAL:
3185c007436SBen Gras warnx("setgroups: ngroups > ngroupsmax");
3195c007436SBen Gras maybe_exit(EXIT_FAILURE);
3205c007436SBen Gras break;
3215c007436SBen Gras case EPERM:
3225c007436SBen Gras case EFAULT:
3235c007436SBen Gras default:
3245c007436SBen Gras warn("setgroups");
3255c007436SBen Gras maybe_exit(EXIT_FAILURE);
3265c007436SBen Gras break;
3275c007436SBen Gras }
3285c007436SBen Gras break;
3295c007436SBen Gras }
3305c007436SBen Gras
3315c007436SBen Gras #ifdef LOGIN_CAP
3325c007436SBen Gras if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGID) == -1)
3335c007436SBen Gras err(EXIT_FAILURE, "setting user context");
3345c007436SBen Gras #else
3355c007436SBen Gras if (setgid(pwd->pw_gid) == -1)
3365c007436SBen Gras err(EXIT_FAILURE, "setgid");
3375c007436SBen Gras #endif
3385c007436SBen Gras }
339