1296047Soshogbo/*- 2296047Soshogbo * Copyright (c) 2013 The FreeBSD Foundation 3296047Soshogbo * All rights reserved. 4296047Soshogbo * 5296047Soshogbo * This software was developed by Pawel Jakub Dawidek under sponsorship from 6296047Soshogbo * the FreeBSD Foundation. 7296047Soshogbo * 8296047Soshogbo * Redistribution and use in source and binary forms, with or without 9296047Soshogbo * modification, are permitted provided that the following conditions 10296047Soshogbo * are met: 11296047Soshogbo * 1. Redistributions of source code must retain the above copyright 12296047Soshogbo * notice, this list of conditions and the following disclaimer. 13296047Soshogbo * 2. Redistributions in binary form must reproduce the above copyright 14296047Soshogbo * notice, this list of conditions and the following disclaimer in the 15296047Soshogbo * documentation and/or other materials provided with the distribution. 16296047Soshogbo * 17296047Soshogbo * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18296047Soshogbo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19296047Soshogbo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20296047Soshogbo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21296047Soshogbo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22296047Soshogbo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23296047Soshogbo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24296047Soshogbo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25296047Soshogbo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26296047Soshogbo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27296047Soshogbo * SUCH DAMAGE. 28296047Soshogbo */ 29296047Soshogbo 30296047Soshogbo#include <sys/cdefs.h> 31296047Soshogbo__FBSDID("$FreeBSD$"); 32296047Soshogbo 33296047Soshogbo#include <sys/dnv.h> 34296047Soshogbo#include <sys/nv.h> 35296047Soshogbo#include <sys/param.h> 36296047Soshogbo 37296047Soshogbo#include <assert.h> 38296047Soshogbo#include <errno.h> 39296047Soshogbo#include <grp.h> 40296047Soshogbo#include <stdlib.h> 41296047Soshogbo#include <string.h> 42296047Soshogbo 43296047Soshogbo#include <libcasper.h> 44296047Soshogbo#include <libcasper_service.h> 45296047Soshogbo 46296047Soshogbo#include "cap_grp.h" 47296047Soshogbo 48296047Soshogbostatic struct group ggrp; 49296047Soshogbostatic char *gbuffer; 50296047Soshogbostatic size_t gbufsize; 51296047Soshogbo 52296047Soshogbostatic int 53296047Soshogbogroup_resize(void) 54296047Soshogbo{ 55296047Soshogbo char *buf; 56296047Soshogbo 57296047Soshogbo if (gbufsize == 0) 58296047Soshogbo gbufsize = 1024; 59296047Soshogbo else 60296047Soshogbo gbufsize *= 2; 61296047Soshogbo 62296047Soshogbo buf = gbuffer; 63296047Soshogbo gbuffer = realloc(buf, gbufsize); 64296047Soshogbo if (gbuffer == NULL) { 65296047Soshogbo free(buf); 66296047Soshogbo gbufsize = 0; 67296047Soshogbo return (ENOMEM); 68296047Soshogbo } 69296047Soshogbo memset(gbuffer, 0, gbufsize); 70296047Soshogbo 71296047Soshogbo return (0); 72296047Soshogbo} 73296047Soshogbo 74296047Soshogbostatic int 75296047Soshogbogroup_unpack_string(const nvlist_t *nvl, const char *fieldname, char **fieldp, 76296047Soshogbo char **bufferp, size_t *bufsizep) 77296047Soshogbo{ 78296047Soshogbo const char *str; 79296047Soshogbo size_t len; 80296047Soshogbo 81296047Soshogbo str = nvlist_get_string(nvl, fieldname); 82296047Soshogbo len = strlcpy(*bufferp, str, *bufsizep); 83296047Soshogbo if (len >= *bufsizep) 84296047Soshogbo return (ERANGE); 85296047Soshogbo *fieldp = *bufferp; 86296047Soshogbo *bufferp += len + 1; 87296047Soshogbo *bufsizep -= len + 1; 88296047Soshogbo 89296047Soshogbo return (0); 90296047Soshogbo} 91296047Soshogbo 92296047Soshogbostatic int 93296047Soshogbogroup_unpack_members(const nvlist_t *nvl, char ***fieldp, char **bufferp, 94296047Soshogbo size_t *bufsizep) 95296047Soshogbo{ 96296047Soshogbo const char *mem; 97296047Soshogbo char **outstrs, *str, nvlname[64]; 98296047Soshogbo size_t nmem, datasize, strsize; 99296047Soshogbo unsigned int ii; 100296047Soshogbo int n; 101296047Soshogbo 102296047Soshogbo if (!nvlist_exists_number(nvl, "gr_nmem")) { 103296047Soshogbo datasize = _ALIGNBYTES + sizeof(char *); 104296047Soshogbo if (datasize >= *bufsizep) 105296047Soshogbo return (ERANGE); 106296047Soshogbo outstrs = (char **)_ALIGN(*bufferp); 107296047Soshogbo outstrs[0] = NULL; 108296047Soshogbo *fieldp = outstrs; 109296047Soshogbo *bufferp += datasize; 110296047Soshogbo *bufsizep -= datasize; 111296047Soshogbo return (0); 112296047Soshogbo } 113296047Soshogbo 114296047Soshogbo nmem = (size_t)nvlist_get_number(nvl, "gr_nmem"); 115296047Soshogbo datasize = _ALIGNBYTES + sizeof(char *) * (nmem + 1); 116296047Soshogbo for (ii = 0; ii < nmem; ii++) { 117296047Soshogbo n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ii); 118296047Soshogbo assert(n > 0 && n < (int)sizeof(nvlname)); 119296047Soshogbo mem = dnvlist_get_string(nvl, nvlname, NULL); 120296047Soshogbo if (mem == NULL) 121296047Soshogbo return (EINVAL); 122296047Soshogbo datasize += strlen(mem) + 1; 123296047Soshogbo } 124296047Soshogbo 125296047Soshogbo if (datasize >= *bufsizep) 126296047Soshogbo return (ERANGE); 127296047Soshogbo 128296047Soshogbo outstrs = (char **)_ALIGN(*bufferp); 129296047Soshogbo str = (char *)outstrs + sizeof(char *) * (nmem + 1); 130296047Soshogbo for (ii = 0; ii < nmem; ii++) { 131296047Soshogbo n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ii); 132296047Soshogbo assert(n > 0 && n < (int)sizeof(nvlname)); 133296047Soshogbo mem = nvlist_get_string(nvl, nvlname); 134296047Soshogbo strsize = strlen(mem) + 1; 135296047Soshogbo memcpy(str, mem, strsize); 136296047Soshogbo outstrs[ii] = str; 137296047Soshogbo str += strsize; 138296047Soshogbo } 139296047Soshogbo assert(ii == nmem); 140296047Soshogbo outstrs[ii] = NULL; 141296047Soshogbo 142296047Soshogbo *fieldp = outstrs; 143296047Soshogbo *bufferp += datasize; 144296047Soshogbo *bufsizep -= datasize; 145296047Soshogbo 146296047Soshogbo return (0); 147296047Soshogbo} 148296047Soshogbo 149296047Soshogbostatic int 150296047Soshogbogroup_unpack(const nvlist_t *nvl, struct group *grp, char *buffer, 151296047Soshogbo size_t bufsize) 152296047Soshogbo{ 153296047Soshogbo int error; 154296047Soshogbo 155296047Soshogbo if (!nvlist_exists_string(nvl, "gr_name")) 156296047Soshogbo return (EINVAL); 157296047Soshogbo 158296047Soshogbo memset(grp, 0, sizeof(*grp)); 159296047Soshogbo 160296047Soshogbo error = group_unpack_string(nvl, "gr_name", &grp->gr_name, &buffer, 161296047Soshogbo &bufsize); 162296047Soshogbo if (error != 0) 163296047Soshogbo return (error); 164296047Soshogbo error = group_unpack_string(nvl, "gr_passwd", &grp->gr_passwd, &buffer, 165296047Soshogbo &bufsize); 166296047Soshogbo if (error != 0) 167296047Soshogbo return (error); 168296047Soshogbo grp->gr_gid = (gid_t)nvlist_get_number(nvl, "gr_gid"); 169296047Soshogbo error = group_unpack_members(nvl, &grp->gr_mem, &buffer, &bufsize); 170296047Soshogbo if (error != 0) 171296047Soshogbo return (error); 172296047Soshogbo 173296047Soshogbo return (0); 174296047Soshogbo} 175296047Soshogbo 176296047Soshogbostatic int 177296047Soshogbocap_getgrcommon_r(cap_channel_t *chan, const char *cmd, const char *name, 178296047Soshogbo gid_t gid, struct group *grp, char *buffer, size_t bufsize, 179296047Soshogbo struct group **result) 180296047Soshogbo{ 181296047Soshogbo nvlist_t *nvl; 182296047Soshogbo bool getgr_r; 183296047Soshogbo int error; 184296047Soshogbo 185296047Soshogbo nvl = nvlist_create(0); 186296047Soshogbo nvlist_add_string(nvl, "cmd", cmd); 187296047Soshogbo if (strcmp(cmd, "getgrent") == 0 || strcmp(cmd, "getgrent_r") == 0) { 188296047Soshogbo /* Add nothing. */ 189296047Soshogbo } else if (strcmp(cmd, "getgrnam") == 0 || 190296047Soshogbo strcmp(cmd, "getgrnam_r") == 0) { 191296047Soshogbo nvlist_add_string(nvl, "name", name); 192296047Soshogbo } else if (strcmp(cmd, "getgrgid") == 0 || 193296047Soshogbo strcmp(cmd, "getgrgid_r") == 0) { 194296047Soshogbo nvlist_add_number(nvl, "gid", (uint64_t)gid); 195296047Soshogbo } else { 196296047Soshogbo abort(); 197296047Soshogbo } 198296047Soshogbo nvl = cap_xfer_nvlist(chan, nvl, 0); 199296047Soshogbo if (nvl == NULL) { 200296047Soshogbo assert(errno != 0); 201296047Soshogbo *result = NULL; 202296047Soshogbo return (errno); 203296047Soshogbo } 204296047Soshogbo error = (int)nvlist_get_number(nvl, "error"); 205296047Soshogbo if (error != 0) { 206296047Soshogbo nvlist_destroy(nvl); 207296047Soshogbo *result = NULL; 208296047Soshogbo return (error); 209296047Soshogbo } 210296047Soshogbo 211296047Soshogbo if (!nvlist_exists_string(nvl, "gr_name")) { 212296047Soshogbo /* Not found. */ 213296047Soshogbo nvlist_destroy(nvl); 214296047Soshogbo *result = NULL; 215296047Soshogbo return (0); 216296047Soshogbo } 217296047Soshogbo 218296047Soshogbo getgr_r = (strcmp(cmd, "getgrent_r") == 0 || 219296047Soshogbo strcmp(cmd, "getgrnam_r") == 0 || strcmp(cmd, "getgrgid_r") == 0); 220296047Soshogbo 221296047Soshogbo for (;;) { 222296047Soshogbo error = group_unpack(nvl, grp, buffer, bufsize); 223296047Soshogbo if (getgr_r || error != ERANGE) 224296047Soshogbo break; 225296047Soshogbo assert(buffer == gbuffer); 226296047Soshogbo assert(bufsize == gbufsize); 227296047Soshogbo error = group_resize(); 228296047Soshogbo if (error != 0) 229296047Soshogbo break; 230296047Soshogbo /* Update pointers after resize. */ 231296047Soshogbo buffer = gbuffer; 232296047Soshogbo bufsize = gbufsize; 233296047Soshogbo } 234296047Soshogbo 235296047Soshogbo nvlist_destroy(nvl); 236296047Soshogbo 237296047Soshogbo if (error == 0) 238296047Soshogbo *result = grp; 239296047Soshogbo else 240296047Soshogbo *result = NULL; 241296047Soshogbo 242296047Soshogbo return (error); 243296047Soshogbo} 244296047Soshogbo 245296047Soshogbostatic struct group * 246296047Soshogbocap_getgrcommon(cap_channel_t *chan, const char *cmd, const char *name, 247296047Soshogbo gid_t gid) 248296047Soshogbo{ 249296047Soshogbo struct group *result; 250296047Soshogbo int error, serrno; 251296047Soshogbo 252296047Soshogbo serrno = errno; 253296047Soshogbo 254296047Soshogbo error = cap_getgrcommon_r(chan, cmd, name, gid, &ggrp, gbuffer, 255296047Soshogbo gbufsize, &result); 256296047Soshogbo if (error != 0) { 257296047Soshogbo errno = error; 258296047Soshogbo return (NULL); 259296047Soshogbo } 260296047Soshogbo 261296047Soshogbo errno = serrno; 262296047Soshogbo 263296047Soshogbo return (result); 264296047Soshogbo} 265296047Soshogbo 266296047Soshogbostruct group * 267296047Soshogbocap_getgrent(cap_channel_t *chan) 268296047Soshogbo{ 269296047Soshogbo 270296047Soshogbo return (cap_getgrcommon(chan, "getgrent", NULL, 0)); 271296047Soshogbo} 272296047Soshogbo 273296047Soshogbostruct group * 274296047Soshogbocap_getgrnam(cap_channel_t *chan, const char *name) 275296047Soshogbo{ 276296047Soshogbo 277296047Soshogbo return (cap_getgrcommon(chan, "getgrnam", name, 0)); 278296047Soshogbo} 279296047Soshogbo 280296047Soshogbostruct group * 281296047Soshogbocap_getgrgid(cap_channel_t *chan, gid_t gid) 282296047Soshogbo{ 283296047Soshogbo 284296047Soshogbo return (cap_getgrcommon(chan, "getgrgid", NULL, gid)); 285296047Soshogbo} 286296047Soshogbo 287296047Soshogboint 288296047Soshogbocap_getgrent_r(cap_channel_t *chan, struct group *grp, char *buffer, 289296047Soshogbo size_t bufsize, struct group **result) 290296047Soshogbo{ 291296047Soshogbo 292296047Soshogbo return (cap_getgrcommon_r(chan, "getgrent_r", NULL, 0, grp, buffer, 293296047Soshogbo bufsize, result)); 294296047Soshogbo} 295296047Soshogbo 296296047Soshogboint 297296047Soshogbocap_getgrnam_r(cap_channel_t *chan, const char *name, struct group *grp, 298296047Soshogbo char *buffer, size_t bufsize, struct group **result) 299296047Soshogbo{ 300296047Soshogbo 301296047Soshogbo return (cap_getgrcommon_r(chan, "getgrnam_r", name, 0, grp, buffer, 302296047Soshogbo bufsize, result)); 303296047Soshogbo} 304296047Soshogbo 305296047Soshogboint 306296047Soshogbocap_getgrgid_r(cap_channel_t *chan, gid_t gid, struct group *grp, char *buffer, 307296047Soshogbo size_t bufsize, struct group **result) 308296047Soshogbo{ 309296047Soshogbo 310296047Soshogbo return (cap_getgrcommon_r(chan, "getgrgid_r", NULL, gid, grp, buffer, 311296047Soshogbo bufsize, result)); 312296047Soshogbo} 313296047Soshogbo 314296047Soshogboint 315296047Soshogbocap_setgroupent(cap_channel_t *chan, int stayopen) 316296047Soshogbo{ 317296047Soshogbo nvlist_t *nvl; 318296047Soshogbo 319296047Soshogbo nvl = nvlist_create(0); 320296047Soshogbo nvlist_add_string(nvl, "cmd", "setgroupent"); 321296047Soshogbo nvlist_add_bool(nvl, "stayopen", stayopen != 0); 322296047Soshogbo nvl = cap_xfer_nvlist(chan, nvl, 0); 323296047Soshogbo if (nvl == NULL) 324296047Soshogbo return (0); 325296047Soshogbo if (nvlist_get_number(nvl, "error") != 0) { 326296047Soshogbo errno = nvlist_get_number(nvl, "error"); 327296047Soshogbo nvlist_destroy(nvl); 328296047Soshogbo return (0); 329296047Soshogbo } 330296047Soshogbo nvlist_destroy(nvl); 331296047Soshogbo 332296047Soshogbo return (1); 333296047Soshogbo} 334296047Soshogbo 335296047Soshogboint 336296047Soshogbocap_setgrent(cap_channel_t *chan) 337296047Soshogbo{ 338296047Soshogbo nvlist_t *nvl; 339296047Soshogbo 340296047Soshogbo nvl = nvlist_create(0); 341296047Soshogbo nvlist_add_string(nvl, "cmd", "setgrent"); 342296047Soshogbo nvl = cap_xfer_nvlist(chan, nvl, 0); 343296047Soshogbo if (nvl == NULL) 344296047Soshogbo return (0); 345296047Soshogbo if (nvlist_get_number(nvl, "error") != 0) { 346296047Soshogbo errno = nvlist_get_number(nvl, "error"); 347296047Soshogbo nvlist_destroy(nvl); 348296047Soshogbo return (0); 349296047Soshogbo } 350296047Soshogbo nvlist_destroy(nvl); 351296047Soshogbo 352296047Soshogbo return (1); 353296047Soshogbo} 354296047Soshogbo 355296047Soshogbovoid 356296047Soshogbocap_endgrent(cap_channel_t *chan) 357296047Soshogbo{ 358296047Soshogbo nvlist_t *nvl; 359296047Soshogbo 360296047Soshogbo nvl = nvlist_create(0); 361296047Soshogbo nvlist_add_string(nvl, "cmd", "endgrent"); 362296047Soshogbo /* Ignore any errors, we have no way to report them. */ 363296047Soshogbo nvlist_destroy(cap_xfer_nvlist(chan, nvl, 0)); 364296047Soshogbo} 365296047Soshogbo 366296047Soshogboint 367296047Soshogbocap_grp_limit_cmds(cap_channel_t *chan, const char * const *cmds, size_t ncmds) 368296047Soshogbo{ 369296047Soshogbo nvlist_t *limits, *nvl; 370296047Soshogbo unsigned int i; 371296047Soshogbo 372296047Soshogbo if (cap_limit_get(chan, &limits) < 0) 373296047Soshogbo return (-1); 374296047Soshogbo if (limits == NULL) { 375296047Soshogbo limits = nvlist_create(0); 376296047Soshogbo } else { 377296047Soshogbo if (nvlist_exists_nvlist(limits, "cmds")) 378296047Soshogbo nvlist_free_nvlist(limits, "cmds"); 379296047Soshogbo } 380296047Soshogbo nvl = nvlist_create(0); 381296047Soshogbo for (i = 0; i < ncmds; i++) 382296047Soshogbo nvlist_add_null(nvl, cmds[i]); 383296047Soshogbo nvlist_move_nvlist(limits, "cmds", nvl); 384296047Soshogbo return (cap_limit_set(chan, limits)); 385296047Soshogbo} 386296047Soshogbo 387296047Soshogboint 388296047Soshogbocap_grp_limit_fields(cap_channel_t *chan, const char * const *fields, 389296047Soshogbo size_t nfields) 390296047Soshogbo{ 391296047Soshogbo nvlist_t *limits, *nvl; 392296047Soshogbo unsigned int i; 393296047Soshogbo 394296047Soshogbo if (cap_limit_get(chan, &limits) < 0) 395296047Soshogbo return (-1); 396296047Soshogbo if (limits == NULL) { 397296047Soshogbo limits = nvlist_create(0); 398296047Soshogbo } else { 399296047Soshogbo if (nvlist_exists_nvlist(limits, "fields")) 400296047Soshogbo nvlist_free_nvlist(limits, "fields"); 401296047Soshogbo } 402296047Soshogbo nvl = nvlist_create(0); 403296047Soshogbo for (i = 0; i < nfields; i++) 404296047Soshogbo nvlist_add_null(nvl, fields[i]); 405296047Soshogbo nvlist_move_nvlist(limits, "fields", nvl); 406296047Soshogbo return (cap_limit_set(chan, limits)); 407296047Soshogbo} 408296047Soshogbo 409296047Soshogboint 410296047Soshogbocap_grp_limit_groups(cap_channel_t *chan, const char * const *names, 411296047Soshogbo size_t nnames, gid_t *gids, size_t ngids) 412296047Soshogbo{ 413296047Soshogbo nvlist_t *limits, *groups; 414296047Soshogbo unsigned int i; 415296047Soshogbo char nvlname[64]; 416296047Soshogbo int n; 417296047Soshogbo 418296047Soshogbo if (cap_limit_get(chan, &limits) < 0) 419296047Soshogbo return (-1); 420296047Soshogbo if (limits == NULL) { 421296047Soshogbo limits = nvlist_create(0); 422296047Soshogbo } else { 423296047Soshogbo if (nvlist_exists_nvlist(limits, "groups")) 424296047Soshogbo nvlist_free_nvlist(limits, "groups"); 425296047Soshogbo } 426296047Soshogbo groups = nvlist_create(0); 427296047Soshogbo for (i = 0; i < ngids; i++) { 428296047Soshogbo n = snprintf(nvlname, sizeof(nvlname), "gid%u", i); 429296047Soshogbo assert(n > 0 && n < (int)sizeof(nvlname)); 430296047Soshogbo nvlist_add_number(groups, nvlname, (uint64_t)gids[i]); 431296047Soshogbo } 432296047Soshogbo for (i = 0; i < nnames; i++) { 433296047Soshogbo n = snprintf(nvlname, sizeof(nvlname), "gid%u", i); 434296047Soshogbo assert(n > 0 && n < (int)sizeof(nvlname)); 435296047Soshogbo nvlist_add_string(groups, nvlname, names[i]); 436296047Soshogbo } 437296047Soshogbo nvlist_move_nvlist(limits, "groups", groups); 438296047Soshogbo return (cap_limit_set(chan, limits)); 439296047Soshogbo} 440296047Soshogbo 441296047Soshogbo/* 442296047Soshogbo * Service functions. 443296047Soshogbo */ 444296047Soshogbostatic bool 445296047Soshogbogrp_allowed_cmd(const nvlist_t *limits, const char *cmd) 446296047Soshogbo{ 447296047Soshogbo 448296047Soshogbo if (limits == NULL) 449296047Soshogbo return (true); 450296047Soshogbo 451296047Soshogbo /* 452296047Soshogbo * If no limit was set on allowed commands, then all commands 453296047Soshogbo * are allowed. 454296047Soshogbo */ 455296047Soshogbo if (!nvlist_exists_nvlist(limits, "cmds")) 456296047Soshogbo return (true); 457296047Soshogbo 458296047Soshogbo limits = nvlist_get_nvlist(limits, "cmds"); 459296047Soshogbo return (nvlist_exists_null(limits, cmd)); 460296047Soshogbo} 461296047Soshogbo 462296047Soshogbostatic int 463296047Soshogbogrp_allowed_cmds(const nvlist_t *oldlimits, const nvlist_t *newlimits) 464296047Soshogbo{ 465296047Soshogbo const char *name; 466296047Soshogbo void *cookie; 467296047Soshogbo int type; 468296047Soshogbo 469296047Soshogbo cookie = NULL; 470296047Soshogbo while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { 471296047Soshogbo if (type != NV_TYPE_NULL) 472296047Soshogbo return (EINVAL); 473296047Soshogbo if (!grp_allowed_cmd(oldlimits, name)) 474296047Soshogbo return (ENOTCAPABLE); 475296047Soshogbo } 476296047Soshogbo 477296047Soshogbo return (0); 478296047Soshogbo} 479296047Soshogbo 480296047Soshogbostatic bool 481296047Soshogbogrp_allowed_group(const nvlist_t *limits, const char *gname, gid_t gid) 482296047Soshogbo{ 483296047Soshogbo const char *name; 484296047Soshogbo void *cookie; 485296047Soshogbo int type; 486296047Soshogbo 487296047Soshogbo if (limits == NULL) 488296047Soshogbo return (true); 489296047Soshogbo 490296047Soshogbo /* 491296047Soshogbo * If no limit was set on allowed groups, then all groups are allowed. 492296047Soshogbo */ 493296047Soshogbo if (!nvlist_exists_nvlist(limits, "groups")) 494296047Soshogbo return (true); 495296047Soshogbo 496296047Soshogbo limits = nvlist_get_nvlist(limits, "groups"); 497296047Soshogbo cookie = NULL; 498296047Soshogbo while ((name = nvlist_next(limits, &type, &cookie)) != NULL) { 499296047Soshogbo switch (type) { 500296047Soshogbo case NV_TYPE_NUMBER: 501296047Soshogbo if (gid != (gid_t)-1 && 502296047Soshogbo nvlist_get_number(limits, name) == (uint64_t)gid) { 503296047Soshogbo return (true); 504296047Soshogbo } 505296047Soshogbo break; 506296047Soshogbo case NV_TYPE_STRING: 507296047Soshogbo if (gname != NULL && 508296047Soshogbo strcmp(nvlist_get_string(limits, name), 509296047Soshogbo gname) == 0) { 510296047Soshogbo return (true); 511296047Soshogbo } 512296047Soshogbo break; 513296047Soshogbo default: 514296047Soshogbo abort(); 515296047Soshogbo } 516296047Soshogbo } 517296047Soshogbo 518296047Soshogbo return (false); 519296047Soshogbo} 520296047Soshogbo 521296047Soshogbostatic int 522296047Soshogbogrp_allowed_groups(const nvlist_t *oldlimits, const nvlist_t *newlimits) 523296047Soshogbo{ 524296047Soshogbo const char *name, *gname; 525296047Soshogbo void *cookie; 526296047Soshogbo gid_t gid; 527296047Soshogbo int type; 528296047Soshogbo 529296047Soshogbo cookie = NULL; 530296047Soshogbo while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { 531296047Soshogbo switch (type) { 532296047Soshogbo case NV_TYPE_NUMBER: 533296047Soshogbo gid = (gid_t)nvlist_get_number(newlimits, name); 534296047Soshogbo gname = NULL; 535296047Soshogbo break; 536296047Soshogbo case NV_TYPE_STRING: 537296047Soshogbo gid = (gid_t)-1; 538296047Soshogbo gname = nvlist_get_string(newlimits, name); 539296047Soshogbo break; 540296047Soshogbo default: 541296047Soshogbo return (EINVAL); 542296047Soshogbo } 543296047Soshogbo if (!grp_allowed_group(oldlimits, gname, gid)) 544296047Soshogbo return (ENOTCAPABLE); 545296047Soshogbo } 546296047Soshogbo 547296047Soshogbo return (0); 548296047Soshogbo} 549296047Soshogbo 550296047Soshogbostatic bool 551296047Soshogbogrp_allowed_field(const nvlist_t *limits, const char *field) 552296047Soshogbo{ 553296047Soshogbo 554296047Soshogbo if (limits == NULL) 555296047Soshogbo return (true); 556296047Soshogbo 557296047Soshogbo /* 558296047Soshogbo * If no limit was set on allowed fields, then all fields are allowed. 559296047Soshogbo */ 560296047Soshogbo if (!nvlist_exists_nvlist(limits, "fields")) 561296047Soshogbo return (true); 562296047Soshogbo 563296047Soshogbo limits = nvlist_get_nvlist(limits, "fields"); 564296047Soshogbo return (nvlist_exists_null(limits, field)); 565296047Soshogbo} 566296047Soshogbo 567296047Soshogbostatic int 568296047Soshogbogrp_allowed_fields(const nvlist_t *oldlimits, const nvlist_t *newlimits) 569296047Soshogbo{ 570296047Soshogbo const char *name; 571296047Soshogbo void *cookie; 572296047Soshogbo int type; 573296047Soshogbo 574296047Soshogbo cookie = NULL; 575296047Soshogbo while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { 576296047Soshogbo if (type != NV_TYPE_NULL) 577296047Soshogbo return (EINVAL); 578296047Soshogbo if (!grp_allowed_field(oldlimits, name)) 579296047Soshogbo return (ENOTCAPABLE); 580296047Soshogbo } 581296047Soshogbo 582296047Soshogbo return (0); 583296047Soshogbo} 584296047Soshogbo 585296047Soshogbostatic bool 586296047Soshogbogrp_pack(const nvlist_t *limits, const struct group *grp, nvlist_t *nvl) 587296047Soshogbo{ 588296047Soshogbo char nvlname[64]; 589296047Soshogbo int n; 590296047Soshogbo 591296047Soshogbo if (grp == NULL) 592296047Soshogbo return (true); 593296047Soshogbo 594296047Soshogbo /* 595296047Soshogbo * If either name or GID is allowed, we allow it. 596296047Soshogbo */ 597296047Soshogbo if (!grp_allowed_group(limits, grp->gr_name, grp->gr_gid)) 598296047Soshogbo return (false); 599296047Soshogbo 600296047Soshogbo if (grp_allowed_field(limits, "gr_name")) 601296047Soshogbo nvlist_add_string(nvl, "gr_name", grp->gr_name); 602296047Soshogbo else 603296047Soshogbo nvlist_add_string(nvl, "gr_name", ""); 604296047Soshogbo if (grp_allowed_field(limits, "gr_passwd")) 605296047Soshogbo nvlist_add_string(nvl, "gr_passwd", grp->gr_passwd); 606296047Soshogbo else 607296047Soshogbo nvlist_add_string(nvl, "gr_passwd", ""); 608296047Soshogbo if (grp_allowed_field(limits, "gr_gid")) 609296047Soshogbo nvlist_add_number(nvl, "gr_gid", (uint64_t)grp->gr_gid); 610296047Soshogbo else 611296047Soshogbo nvlist_add_number(nvl, "gr_gid", (uint64_t)-1); 612296047Soshogbo if (grp_allowed_field(limits, "gr_mem") && grp->gr_mem[0] != NULL) { 613296047Soshogbo unsigned int ngroups; 614296047Soshogbo 615296047Soshogbo for (ngroups = 0; grp->gr_mem[ngroups] != NULL; ngroups++) { 616296047Soshogbo n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", 617296047Soshogbo ngroups); 618296047Soshogbo assert(n > 0 && n < (ssize_t)sizeof(nvlname)); 619296047Soshogbo nvlist_add_string(nvl, nvlname, grp->gr_mem[ngroups]); 620296047Soshogbo } 621296047Soshogbo nvlist_add_number(nvl, "gr_nmem", (uint64_t)ngroups); 622296047Soshogbo } 623296047Soshogbo 624296047Soshogbo return (true); 625296047Soshogbo} 626296047Soshogbo 627296047Soshogbostatic int 628296047Soshogbogrp_getgrent(const nvlist_t *limits, const nvlist_t *nvlin __unused, 629296047Soshogbo nvlist_t *nvlout) 630296047Soshogbo{ 631296047Soshogbo struct group *grp; 632296047Soshogbo 633296047Soshogbo for (;;) { 634296047Soshogbo errno = 0; 635296047Soshogbo grp = getgrent(); 636296047Soshogbo if (errno != 0) 637296047Soshogbo return (errno); 638296047Soshogbo if (grp_pack(limits, grp, nvlout)) 639296047Soshogbo return (0); 640296047Soshogbo } 641296047Soshogbo 642296047Soshogbo /* NOTREACHED */ 643296047Soshogbo} 644296047Soshogbo 645296047Soshogbostatic int 646296047Soshogbogrp_getgrnam(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) 647296047Soshogbo{ 648296047Soshogbo struct group *grp; 649296047Soshogbo const char *name; 650296047Soshogbo 651296047Soshogbo if (!nvlist_exists_string(nvlin, "name")) 652296047Soshogbo return (EINVAL); 653296047Soshogbo name = nvlist_get_string(nvlin, "name"); 654296047Soshogbo assert(name != NULL); 655296047Soshogbo 656296047Soshogbo errno = 0; 657296047Soshogbo grp = getgrnam(name); 658296047Soshogbo if (errno != 0) 659296047Soshogbo return (errno); 660296047Soshogbo 661296047Soshogbo (void)grp_pack(limits, grp, nvlout); 662296047Soshogbo 663296047Soshogbo return (0); 664296047Soshogbo} 665296047Soshogbo 666296047Soshogbostatic int 667296047Soshogbogrp_getgrgid(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) 668296047Soshogbo{ 669296047Soshogbo struct group *grp; 670296047Soshogbo gid_t gid; 671296047Soshogbo 672296047Soshogbo if (!nvlist_exists_number(nvlin, "gid")) 673296047Soshogbo return (EINVAL); 674296047Soshogbo 675296047Soshogbo gid = (gid_t)nvlist_get_number(nvlin, "gid"); 676296047Soshogbo 677296047Soshogbo errno = 0; 678296047Soshogbo grp = getgrgid(gid); 679296047Soshogbo if (errno != 0) 680296047Soshogbo return (errno); 681296047Soshogbo 682296047Soshogbo (void)grp_pack(limits, grp, nvlout); 683296047Soshogbo 684296047Soshogbo return (0); 685296047Soshogbo} 686296047Soshogbo 687296047Soshogbostatic int 688296047Soshogbogrp_setgroupent(const nvlist_t *limits __unused, const nvlist_t *nvlin, 689296047Soshogbo nvlist_t *nvlout __unused) 690296047Soshogbo{ 691296047Soshogbo int stayopen; 692296047Soshogbo 693296047Soshogbo if (!nvlist_exists_bool(nvlin, "stayopen")) 694296047Soshogbo return (EINVAL); 695296047Soshogbo 696296047Soshogbo stayopen = nvlist_get_bool(nvlin, "stayopen") ? 1 : 0; 697296047Soshogbo 698296047Soshogbo return (setgroupent(stayopen) == 0 ? EFAULT : 0); 699296047Soshogbo} 700296047Soshogbo 701296047Soshogbostatic int 702296047Soshogbogrp_setgrent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused, 703296047Soshogbo nvlist_t *nvlout __unused) 704296047Soshogbo{ 705296047Soshogbo 706301167Sed setgrent(); 707301167Sed 708301167Sed return (0); 709296047Soshogbo} 710296047Soshogbo 711296047Soshogbostatic int 712296047Soshogbogrp_endgrent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused, 713296047Soshogbo nvlist_t *nvlout __unused) 714296047Soshogbo{ 715296047Soshogbo 716296047Soshogbo endgrent(); 717296047Soshogbo 718296047Soshogbo return (0); 719296047Soshogbo} 720296047Soshogbo 721296047Soshogbostatic int 722296047Soshogbogrp_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) 723296047Soshogbo{ 724296047Soshogbo const nvlist_t *limits; 725296047Soshogbo const char *name; 726296047Soshogbo void *cookie; 727296047Soshogbo int error, type; 728296047Soshogbo 729296047Soshogbo if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "cmds") && 730296047Soshogbo !nvlist_exists_nvlist(newlimits, "cmds")) { 731296047Soshogbo return (ENOTCAPABLE); 732296047Soshogbo } 733296047Soshogbo if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "fields") && 734296047Soshogbo !nvlist_exists_nvlist(newlimits, "fields")) { 735296047Soshogbo return (ENOTCAPABLE); 736296047Soshogbo } 737296047Soshogbo if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "groups") && 738296047Soshogbo !nvlist_exists_nvlist(newlimits, "groups")) { 739296047Soshogbo return (ENOTCAPABLE); 740296047Soshogbo } 741296047Soshogbo 742296047Soshogbo cookie = NULL; 743296047Soshogbo while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { 744296047Soshogbo if (type != NV_TYPE_NVLIST) 745296047Soshogbo return (EINVAL); 746296047Soshogbo limits = nvlist_get_nvlist(newlimits, name); 747296047Soshogbo if (strcmp(name, "cmds") == 0) 748296047Soshogbo error = grp_allowed_cmds(oldlimits, limits); 749296047Soshogbo else if (strcmp(name, "fields") == 0) 750296047Soshogbo error = grp_allowed_fields(oldlimits, limits); 751296047Soshogbo else if (strcmp(name, "groups") == 0) 752296047Soshogbo error = grp_allowed_groups(oldlimits, limits); 753296047Soshogbo else 754296047Soshogbo error = EINVAL; 755296047Soshogbo if (error != 0) 756296047Soshogbo return (error); 757296047Soshogbo } 758296047Soshogbo 759296047Soshogbo return (0); 760296047Soshogbo} 761296047Soshogbo 762296047Soshogbostatic int 763296047Soshogbogrp_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, 764296047Soshogbo nvlist_t *nvlout) 765296047Soshogbo{ 766296047Soshogbo int error; 767296047Soshogbo 768296047Soshogbo if (!grp_allowed_cmd(limits, cmd)) 769296047Soshogbo return (ENOTCAPABLE); 770296047Soshogbo 771296047Soshogbo if (strcmp(cmd, "getgrent") == 0 || strcmp(cmd, "getgrent_r") == 0) 772296047Soshogbo error = grp_getgrent(limits, nvlin, nvlout); 773296047Soshogbo else if (strcmp(cmd, "getgrnam") == 0 || strcmp(cmd, "getgrnam_r") == 0) 774296047Soshogbo error = grp_getgrnam(limits, nvlin, nvlout); 775296047Soshogbo else if (strcmp(cmd, "getgrgid") == 0 || strcmp(cmd, "getgrgid_r") == 0) 776296047Soshogbo error = grp_getgrgid(limits, nvlin, nvlout); 777296047Soshogbo else if (strcmp(cmd, "setgroupent") == 0) 778296047Soshogbo error = grp_setgroupent(limits, nvlin, nvlout); 779296047Soshogbo else if (strcmp(cmd, "setgrent") == 0) 780296047Soshogbo error = grp_setgrent(limits, nvlin, nvlout); 781296047Soshogbo else if (strcmp(cmd, "endgrent") == 0) 782296047Soshogbo error = grp_endgrent(limits, nvlin, nvlout); 783296047Soshogbo else 784296047Soshogbo error = EINVAL; 785296047Soshogbo 786296047Soshogbo return (error); 787296047Soshogbo} 788296047Soshogbo 789301572SoshogboCREATE_SERVICE("system.grp", grp_limit, grp_command, 0); 790