Index: head/lib/libcapsicum/libcapsicum_dns.c =================================================================== --- head/lib/libcapsicum/libcapsicum_dns.c (revision 282251) +++ head/lib/libcapsicum/libcapsicum_dns.c (revision 282252) @@ -1,347 +1,365 @@ /*- * Copyright (c) 2012-2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * 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. * 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 AUTHORS AND CONTRIBUTORS ``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 AUTHORS OR CONTRIBUTORS 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 "libcapsicum.h" #include "libcapsicum_dns.h" static struct hostent hent; static void hostent_free(struct hostent *hp) { unsigned int ii; free(hp->h_name); hp->h_name = NULL; if (hp->h_aliases != NULL) { for (ii = 0; hp->h_aliases[ii] != NULL; ii++) free(hp->h_aliases[ii]); free(hp->h_aliases); hp->h_aliases = NULL; } if (hp->h_addr_list != NULL) { for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) free(hp->h_addr_list[ii]); free(hp->h_addr_list); hp->h_addr_list = NULL; } } static struct hostent * hostent_unpack(const nvlist_t *nvl, struct hostent *hp) { unsigned int ii, nitems; + char nvlname[64]; + int n; hostent_free(hp); hp->h_name = strdup(nvlist_get_string(nvl, "name")); if (hp->h_name == NULL) goto fail; hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype"); hp->h_length = (int)nvlist_get_number(nvl, "length"); nitems = (unsigned int)nvlist_get_number(nvl, "naliases"); hp->h_aliases = calloc(sizeof(hp->h_aliases[0]), nitems + 1); if (hp->h_aliases == NULL) goto fail; for (ii = 0; ii < nitems; ii++) { + n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii); + assert(n > 0 && n < (int)sizeof(nvlname)); hp->h_aliases[ii] = - strdup(nvlist_getf_string(nvl, "alias%u", ii)); + strdup(nvlist_get_string(nvl, nvlname)); if (hp->h_aliases[ii] == NULL) goto fail; } hp->h_aliases[ii] = NULL; nitems = (unsigned int)nvlist_get_number(nvl, "naddrs"); hp->h_addr_list = calloc(sizeof(hp->h_addr_list[0]), nitems + 1); if (hp->h_addr_list == NULL) goto fail; for (ii = 0; ii < nitems; ii++) { hp->h_addr_list[ii] = malloc(hp->h_length); if (hp->h_addr_list[ii] == NULL) goto fail; - bcopy(nvlist_getf_binary(nvl, NULL, "addr%u", ii), + n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii); + assert(n > 0 && n < (int)sizeof(nvlname)); + bcopy(nvlist_get_binary(nvl, nvlname, NULL), hp->h_addr_list[ii], hp->h_length); } hp->h_addr_list[ii] = NULL; return (hp); fail: hostent_free(hp); h_errno = NO_RECOVERY; return (NULL); } struct hostent * cap_gethostbyname(cap_channel_t *chan, const char *name) { return (cap_gethostbyname2(chan, name, AF_INET)); } struct hostent * cap_gethostbyname2(cap_channel_t *chan, const char *name, int type) { struct hostent *hp; nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "gethostbyname"); nvlist_add_number(nvl, "family", (uint64_t)type); nvlist_add_string(nvl, "name", name); nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) { h_errno = NO_RECOVERY; return (NULL); } if (nvlist_get_number(nvl, "error") != 0) { h_errno = (int)nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (NULL); } hp = hostent_unpack(nvl, &hent); nvlist_destroy(nvl); return (hp); } struct hostent * cap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len, int type) { struct hostent *hp; nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "gethostbyaddr"); nvlist_add_binary(nvl, "addr", addr, (size_t)len); nvlist_add_number(nvl, "family", (uint64_t)type); nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) { h_errno = NO_RECOVERY; return (NULL); } if (nvlist_get_number(nvl, "error") != 0) { h_errno = (int)nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (NULL); } hp = hostent_unpack(nvl, &hent); nvlist_destroy(nvl); return (hp); } static struct addrinfo * addrinfo_unpack(const nvlist_t *nvl) { struct addrinfo *ai; const void *addr; size_t addrlen; const char *canonname; addr = nvlist_get_binary(nvl, "ai_addr", &addrlen); ai = malloc(sizeof(*ai) + addrlen); if (ai == NULL) return (NULL); ai->ai_flags = (int)nvlist_get_number(nvl, "ai_flags"); ai->ai_family = (int)nvlist_get_number(nvl, "ai_family"); ai->ai_socktype = (int)nvlist_get_number(nvl, "ai_socktype"); ai->ai_protocol = (int)nvlist_get_number(nvl, "ai_protocol"); ai->ai_addrlen = (socklen_t)addrlen; canonname = nvlist_get_string(nvl, "ai_canonname"); if (canonname != NULL) { ai->ai_canonname = strdup(canonname); if (ai->ai_canonname == NULL) { free(ai); return (NULL); } } else { ai->ai_canonname = NULL; } ai->ai_addr = (void *)(ai + 1); bcopy(addr, ai->ai_addr, addrlen); ai->ai_next = NULL; return (ai); } int cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { struct addrinfo *firstai, *prevai, *curai; unsigned int ii; const nvlist_t *nvlai; + char nvlname[64]; nvlist_t *nvl; - int error; + int error, n; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "getaddrinfo"); nvlist_add_string(nvl, "hostname", hostname); nvlist_add_string(nvl, "servname", servname); if (hints != NULL) { nvlist_add_number(nvl, "hints.ai_flags", (uint64_t)hints->ai_flags); nvlist_add_number(nvl, "hints.ai_family", (uint64_t)hints->ai_family); nvlist_add_number(nvl, "hints.ai_socktype", (uint64_t)hints->ai_socktype); nvlist_add_number(nvl, "hints.ai_protocol", (uint64_t)hints->ai_protocol); } nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) return (EAI_MEMORY); if (nvlist_get_number(nvl, "error") != 0) { error = (int)nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (error); } nvlai = NULL; firstai = prevai = curai = NULL; for (ii = 0; ; ii++) { - if (!nvlist_existsf_nvlist(nvl, "res%u", ii)) + n = snprintf(nvlname, sizeof(nvlname), "res%u", ii); + assert(n > 0 && n < (int)sizeof(nvlname)); + if (!nvlist_exists_nvlist(nvl, nvlname)) break; - nvlai = nvlist_getf_nvlist(nvl, "res%u", ii); + nvlai = nvlist_get_nvlist(nvl, nvlname); curai = addrinfo_unpack(nvlai); if (curai == NULL) break; if (prevai != NULL) prevai->ai_next = curai; else if (firstai == NULL) firstai = curai; prevai = curai; } nvlist_destroy(nvl); if (curai == NULL && nvlai != NULL) { if (firstai == NULL) freeaddrinfo(firstai); return (EAI_MEMORY); } *res = firstai; return (0); } int cap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) { nvlist_t *nvl; int error; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "getnameinfo"); nvlist_add_number(nvl, "hostlen", (uint64_t)hostlen); nvlist_add_number(nvl, "servlen", (uint64_t)servlen); nvlist_add_binary(nvl, "sa", sa, (size_t)salen); nvlist_add_number(nvl, "flags", (uint64_t)flags); nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) return (EAI_MEMORY); if (nvlist_get_number(nvl, "error") != 0) { error = (int)nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (error); } if (host != NULL) strlcpy(host, nvlist_get_string(nvl, "host"), hostlen + 1); if (serv != NULL) strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen + 1); nvlist_destroy(nvl); return (0); } static void limit_remove(nvlist_t *limits, const char *prefix) { const char *name; size_t prefixlen; void *cookie; prefixlen = strlen(prefix); again: cookie = NULL; while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) { if (strncmp(name, prefix, prefixlen) == 0) { nvlist_free(limits, name); goto again; } } } int cap_dns_type_limit(cap_channel_t *chan, const char * const *types, size_t ntypes) { nvlist_t *limits; unsigned int i; + char nvlname[64]; + int n; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) limits = nvlist_create(0); else limit_remove(limits, "type"); - for (i = 0; i < ntypes; i++) - nvlist_addf_string(limits, types[i], "type%u", i); + for (i = 0; i < ntypes; i++) { + n = snprintf(nvlname, sizeof(nvlname), "type%u", i); + assert(n > 0 && n < (int)sizeof(nvlname)); + nvlist_add_string(limits, nvlname, types[i]); + } return (cap_limit_set(chan, limits)); } int cap_dns_family_limit(cap_channel_t *chan, const int *families, size_t nfamilies) { nvlist_t *limits; unsigned int i; + char nvlname[64]; + int n; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) limits = nvlist_create(0); else limit_remove(limits, "family"); for (i = 0; i < nfamilies; i++) { - nvlist_addf_number(limits, (uint64_t)families[i], - "family%u", i); + n = snprintf(nvlname, sizeof(nvlname), "type%u", i); + assert(n > 0 && n < (int)sizeof(nvlname)); + nvlist_add_number(limits, nvlname, (uint64_t)families[i]); } return (cap_limit_set(chan, limits)); } Index: head/lib/libcapsicum/libcapsicum_grp.c =================================================================== --- head/lib/libcapsicum/libcapsicum_grp.c (revision 282251) +++ head/lib/libcapsicum/libcapsicum_grp.c (revision 282252) @@ -1,426 +1,439 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * 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. * 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 AUTHORS AND CONTRIBUTORS ``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 AUTHORS OR CONTRIBUTORS 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 #include #include "libcapsicum.h" #include "libcapsicum_grp.h" static struct group ggrp; static char *gbuffer; static size_t gbufsize; static int group_resize(void) { char *buf; if (gbufsize == 0) gbufsize = 1024; else gbufsize *= 2; buf = gbuffer; gbuffer = realloc(buf, gbufsize); if (gbuffer == NULL) { free(buf); gbufsize = 0; return (ENOMEM); } memset(gbuffer, 0, gbufsize); return (0); } static int group_unpack_string(const nvlist_t *nvl, const char *fieldname, char **fieldp, char **bufferp, size_t *bufsizep) { const char *str; size_t len; str = nvlist_get_string(nvl, fieldname); len = strlcpy(*bufferp, str, *bufsizep); if (len >= *bufsizep) return (ERANGE); *fieldp = *bufferp; *bufferp += len + 1; *bufsizep -= len + 1; return (0); } static int group_unpack_members(const nvlist_t *nvl, char ***fieldp, char **bufferp, size_t *bufsizep) { const char *mem; - char **outstrs, *str; + char **outstrs, *str, nvlname[64]; size_t nmem, datasize, strsize; unsigned int ii; + int n; if (!nvlist_exists_number(nvl, "gr_nmem")) { datasize = _ALIGNBYTES + sizeof(char *); if (datasize >= *bufsizep) return (ERANGE); outstrs = (char **)_ALIGN(*bufferp); outstrs[0] = NULL; *fieldp = outstrs; *bufferp += datasize; *bufsizep -= datasize; return (0); } nmem = (size_t)nvlist_get_number(nvl, "gr_nmem"); datasize = _ALIGNBYTES + sizeof(char *) * (nmem + 1); for (ii = 0; ii < nmem; ii++) { - mem = dnvlist_getf_string(nvl, NULL, "gr_mem[%u]", ii); + n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ii); + assert(n > 0 && n < (int)sizeof(nvlname)); + mem = dnvlist_get_string(nvl, nvlname, NULL); if (mem == NULL) return (EINVAL); datasize += strlen(mem) + 1; } if (datasize >= *bufsizep) return (ERANGE); outstrs = (char **)_ALIGN(*bufferp); str = (char *)outstrs + sizeof(char *) * (nmem + 1); for (ii = 0; ii < nmem; ii++) { - mem = nvlist_getf_string(nvl, "gr_mem[%u]", ii); + n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ii); + assert(n > 0 && n < (int)sizeof(nvlname)); + mem = nvlist_get_string(nvl, nvlname); strsize = strlen(mem) + 1; memcpy(str, mem, strsize); outstrs[ii] = str; str += strsize; } assert(ii == nmem); outstrs[ii] = NULL; *fieldp = outstrs; *bufferp += datasize; *bufsizep -= datasize; return (0); } static int group_unpack(const nvlist_t *nvl, struct group *grp, char *buffer, size_t bufsize) { int error; if (!nvlist_exists_string(nvl, "gr_name")) return (EINVAL); memset(grp, 0, sizeof(*grp)); error = group_unpack_string(nvl, "gr_name", &grp->gr_name, &buffer, &bufsize); if (error != 0) return (error); error = group_unpack_string(nvl, "gr_passwd", &grp->gr_passwd, &buffer, &bufsize); if (error != 0) return (error); grp->gr_gid = (gid_t)nvlist_get_number(nvl, "gr_gid"); error = group_unpack_members(nvl, &grp->gr_mem, &buffer, &bufsize); if (error != 0) return (error); return (0); } static int cap_getgrcommon_r(cap_channel_t *chan, const char *cmd, const char *name, gid_t gid, struct group *grp, char *buffer, size_t bufsize, struct group **result) { nvlist_t *nvl; bool getgr_r; int error; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", cmd); if (strcmp(cmd, "getgrent") == 0 || strcmp(cmd, "getgrent_r") == 0) { /* Add nothing. */ } else if (strcmp(cmd, "getgrnam") == 0 || strcmp(cmd, "getgrnam_r") == 0) { nvlist_add_string(nvl, "name", name); } else if (strcmp(cmd, "getgrgid") == 0 || strcmp(cmd, "getgrgid_r") == 0) { nvlist_add_number(nvl, "gid", (uint64_t)gid); } else { abort(); } nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) { assert(errno != 0); *result = NULL; return (errno); } error = (int)nvlist_get_number(nvl, "error"); if (error != 0) { nvlist_destroy(nvl); *result = NULL; return (error); } if (!nvlist_exists_string(nvl, "gr_name")) { /* Not found. */ nvlist_destroy(nvl); *result = NULL; return (0); } getgr_r = (strcmp(cmd, "getgrent_r") == 0 || strcmp(cmd, "getgrnam_r") == 0 || strcmp(cmd, "getgrgid_r") == 0); for (;;) { error = group_unpack(nvl, grp, buffer, bufsize); if (getgr_r || error != ERANGE) break; assert(buffer == gbuffer); assert(bufsize == gbufsize); error = group_resize(); if (error != 0) break; /* Update pointers after resize. */ buffer = gbuffer; bufsize = gbufsize; } nvlist_destroy(nvl); if (error == 0) *result = grp; else *result = NULL; return (error); } static struct group * cap_getgrcommon(cap_channel_t *chan, const char *cmd, const char *name, gid_t gid) { struct group *result; int error, serrno; serrno = errno; error = cap_getgrcommon_r(chan, cmd, name, gid, &ggrp, gbuffer, gbufsize, &result); if (error != 0) { errno = error; return (NULL); } errno = serrno; return (result); } struct group * cap_getgrent(cap_channel_t *chan) { return (cap_getgrcommon(chan, "getgrent", NULL, 0)); } struct group * cap_getgrnam(cap_channel_t *chan, const char *name) { return (cap_getgrcommon(chan, "getgrnam", name, 0)); } struct group * cap_getgrgid(cap_channel_t *chan, gid_t gid) { return (cap_getgrcommon(chan, "getgrgid", NULL, gid)); } int cap_getgrent_r(cap_channel_t *chan, struct group *grp, char *buffer, size_t bufsize, struct group **result) { return (cap_getgrcommon_r(chan, "getgrent_r", NULL, 0, grp, buffer, bufsize, result)); } int cap_getgrnam_r(cap_channel_t *chan, const char *name, struct group *grp, char *buffer, size_t bufsize, struct group **result) { return (cap_getgrcommon_r(chan, "getgrnam_r", name, 0, grp, buffer, bufsize, result)); } int cap_getgrgid_r(cap_channel_t *chan, gid_t gid, struct group *grp, char *buffer, size_t bufsize, struct group **result) { return (cap_getgrcommon_r(chan, "getgrgid_r", NULL, gid, grp, buffer, bufsize, result)); } int cap_setgroupent(cap_channel_t *chan, int stayopen) { nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "setgroupent"); nvlist_add_bool(nvl, "stayopen", stayopen != 0); nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) return (0); if (nvlist_get_number(nvl, "error") != 0) { errno = nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (0); } nvlist_destroy(nvl); return (1); } int cap_setgrent(cap_channel_t *chan) { nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "setgrent"); nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) return (0); if (nvlist_get_number(nvl, "error") != 0) { errno = nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (0); } nvlist_destroy(nvl); return (1); } void cap_endgrent(cap_channel_t *chan) { nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "endgrent"); /* Ignore any errors, we have no way to report them. */ nvlist_destroy(cap_xfer_nvlist(chan, nvl)); } int cap_grp_limit_cmds(cap_channel_t *chan, const char * const *cmds, size_t ncmds) { nvlist_t *limits, *nvl; unsigned int i; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) { limits = nvlist_create(0); } else { if (nvlist_exists_nvlist(limits, "cmds")) nvlist_free_nvlist(limits, "cmds"); } nvl = nvlist_create(0); for (i = 0; i < ncmds; i++) nvlist_add_null(nvl, cmds[i]); nvlist_move_nvlist(limits, "cmds", nvl); return (cap_limit_set(chan, limits)); } int cap_grp_limit_fields(cap_channel_t *chan, const char * const *fields, size_t nfields) { nvlist_t *limits, *nvl; unsigned int i; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) { limits = nvlist_create(0); } else { if (nvlist_exists_nvlist(limits, "fields")) nvlist_free_nvlist(limits, "fields"); } nvl = nvlist_create(0); for (i = 0; i < nfields; i++) nvlist_add_null(nvl, fields[i]); nvlist_move_nvlist(limits, "fields", nvl); return (cap_limit_set(chan, limits)); } int cap_grp_limit_groups(cap_channel_t *chan, const char * const *names, size_t nnames, gid_t *gids, size_t ngids) { nvlist_t *limits, *groups; unsigned int i; + char nvlname[64]; + int n; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) { limits = nvlist_create(0); } else { if (nvlist_exists_nvlist(limits, "groups")) nvlist_free_nvlist(limits, "groups"); } groups = nvlist_create(0); - for (i = 0; i < ngids; i++) - nvlist_addf_number(groups, (uint64_t)gids[i], "gid%u", i); - for (i = 0; i < nnames; i++) - nvlist_addf_string(groups, names[i], "name%u", i); + for (i = 0; i < ngids; i++) { + n = snprintf(nvlname, sizeof(nvlname), "gid%u", i); + assert(n > 0 && n < (int)sizeof(nvlname)); + nvlist_add_number(groups, nvlname, (uint64_t)gids[i]); + } + for (i = 0; i < nnames; i++) { + n = snprintf(nvlname, sizeof(nvlname), "gid%u", i); + assert(n > 0 && n < (int)sizeof(nvlname)); + nvlist_add_string(groups, nvlname, names[i]); + } nvlist_move_nvlist(limits, "groups", groups); return (cap_limit_set(chan, limits)); } Index: head/lib/libcapsicum/libcapsicum_pwd.c =================================================================== --- head/lib/libcapsicum/libcapsicum_pwd.c (revision 282251) +++ head/lib/libcapsicum/libcapsicum_pwd.c (revision 282252) @@ -1,384 +1,392 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * 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. * 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 AUTHORS AND CONTRIBUTORS ``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 AUTHORS OR CONTRIBUTORS 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 #include "libcapsicum.h" #include "libcapsicum_pwd.h" static struct passwd gpwd; static char *gbuffer; static size_t gbufsize; static int passwd_resize(void) { char *buf; if (gbufsize == 0) gbufsize = 1024; else gbufsize *= 2; buf = gbuffer; gbuffer = realloc(buf, gbufsize); if (gbuffer == NULL) { free(buf); gbufsize = 0; return (ENOMEM); } memset(gbuffer, 0, gbufsize); return (0); } static int passwd_unpack_string(const nvlist_t *nvl, const char *fieldname, char **fieldp, char **bufferp, size_t *bufsizep) { const char *str; size_t len; str = nvlist_get_string(nvl, fieldname); len = strlcpy(*bufferp, str, *bufsizep); if (len >= *bufsizep) return (ERANGE); *fieldp = *bufferp; *bufferp += len + 1; *bufsizep -= len + 1; return (0); } static int passwd_unpack(const nvlist_t *nvl, struct passwd *pwd, char *buffer, size_t bufsize) { int error; if (!nvlist_exists_string(nvl, "pw_name")) return (EINVAL); memset(pwd, 0, sizeof(*pwd)); error = passwd_unpack_string(nvl, "pw_name", &pwd->pw_name, &buffer, &bufsize); if (error != 0) return (error); pwd->pw_uid = (uid_t)nvlist_get_number(nvl, "pw_uid"); pwd->pw_gid = (gid_t)nvlist_get_number(nvl, "pw_gid"); pwd->pw_change = (time_t)nvlist_get_number(nvl, "pw_change"); error = passwd_unpack_string(nvl, "pw_passwd", &pwd->pw_passwd, &buffer, &bufsize); if (error != 0) return (error); error = passwd_unpack_string(nvl, "pw_class", &pwd->pw_class, &buffer, &bufsize); if (error != 0) return (error); error = passwd_unpack_string(nvl, "pw_gecos", &pwd->pw_gecos, &buffer, &bufsize); if (error != 0) return (error); error = passwd_unpack_string(nvl, "pw_dir", &pwd->pw_dir, &buffer, &bufsize); if (error != 0) return (error); error = passwd_unpack_string(nvl, "pw_shell", &pwd->pw_shell, &buffer, &bufsize); if (error != 0) return (error); pwd->pw_expire = (time_t)nvlist_get_number(nvl, "pw_expire"); pwd->pw_fields = (int)nvlist_get_number(nvl, "pw_fields"); return (0); } static int cap_getpwcommon_r(cap_channel_t *chan, const char *cmd, const char *login, uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { nvlist_t *nvl; bool getpw_r; int error; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", cmd); if (strcmp(cmd, "getpwent") == 0 || strcmp(cmd, "getpwent_r") == 0) { /* Add nothing. */ } else if (strcmp(cmd, "getpwnam") == 0 || strcmp(cmd, "getpwnam_r") == 0) { nvlist_add_string(nvl, "name", login); } else if (strcmp(cmd, "getpwuid") == 0 || strcmp(cmd, "getpwuid_r") == 0) { nvlist_add_number(nvl, "uid", (uint64_t)uid); } else { abort(); } nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) { assert(errno != 0); *result = NULL; return (errno); } error = (int)nvlist_get_number(nvl, "error"); if (error != 0) { nvlist_destroy(nvl); *result = NULL; return (error); } if (!nvlist_exists_string(nvl, "pw_name")) { /* Not found. */ nvlist_destroy(nvl); *result = NULL; return (0); } getpw_r = (strcmp(cmd, "getpwent_r") == 0 || strcmp(cmd, "getpwnam_r") == 0 || strcmp(cmd, "getpwuid_r") == 0); for (;;) { error = passwd_unpack(nvl, pwd, buffer, bufsize); if (getpw_r || error != ERANGE) break; assert(buffer == gbuffer); assert(bufsize == gbufsize); error = passwd_resize(); if (error != 0) break; /* Update pointers after resize. */ buffer = gbuffer; bufsize = gbufsize; } nvlist_destroy(nvl); if (error == 0) *result = pwd; else *result = NULL; return (error); } static struct passwd * cap_getpwcommon(cap_channel_t *chan, const char *cmd, const char *login, uid_t uid) { struct passwd *result; int error, serrno; serrno = errno; error = cap_getpwcommon_r(chan, cmd, login, uid, &gpwd, gbuffer, gbufsize, &result); if (error != 0) { errno = error; return (NULL); } errno = serrno; return (result); } struct passwd * cap_getpwent(cap_channel_t *chan) { return (cap_getpwcommon(chan, "getpwent", NULL, 0)); } struct passwd * cap_getpwnam(cap_channel_t *chan, const char *login) { return (cap_getpwcommon(chan, "getpwnam", login, 0)); } struct passwd * cap_getpwuid(cap_channel_t *chan, uid_t uid) { return (cap_getpwcommon(chan, "getpwuid", NULL, uid)); } int cap_getpwent_r(cap_channel_t *chan, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { return (cap_getpwcommon_r(chan, "getpwent_r", NULL, 0, pwd, buffer, bufsize, result)); } int cap_getpwnam_r(cap_channel_t *chan, const char *name, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { return (cap_getpwcommon_r(chan, "getpwnam_r", name, 0, pwd, buffer, bufsize, result)); } int cap_getpwuid_r(cap_channel_t *chan, uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { return (cap_getpwcommon_r(chan, "getpwuid_r", NULL, uid, pwd, buffer, bufsize, result)); } int cap_setpassent(cap_channel_t *chan, int stayopen) { nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "setpassent"); nvlist_add_bool(nvl, "stayopen", stayopen != 0); nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) return (0); if (nvlist_get_number(nvl, "error") != 0) { errno = nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (0); } nvlist_destroy(nvl); return (1); } static void cap_set_end_pwent(cap_channel_t *chan, const char *cmd) { nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", cmd); /* Ignore any errors, we have no way to report them. */ nvlist_destroy(cap_xfer_nvlist(chan, nvl)); } void cap_setpwent(cap_channel_t *chan) { cap_set_end_pwent(chan, "setpwent"); } void cap_endpwent(cap_channel_t *chan) { cap_set_end_pwent(chan, "endpwent"); } int cap_pwd_limit_cmds(cap_channel_t *chan, const char * const *cmds, size_t ncmds) { nvlist_t *limits, *nvl; unsigned int i; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) { limits = nvlist_create(0); } else { if (nvlist_exists_nvlist(limits, "cmds")) nvlist_free_nvlist(limits, "cmds"); } nvl = nvlist_create(0); for (i = 0; i < ncmds; i++) nvlist_add_null(nvl, cmds[i]); nvlist_move_nvlist(limits, "cmds", nvl); return (cap_limit_set(chan, limits)); } int cap_pwd_limit_fields(cap_channel_t *chan, const char * const *fields, size_t nfields) { nvlist_t *limits, *nvl; unsigned int i; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) { limits = nvlist_create(0); } else { if (nvlist_exists_nvlist(limits, "fields")) nvlist_free_nvlist(limits, "fields"); } nvl = nvlist_create(0); for (i = 0; i < nfields; i++) nvlist_add_null(nvl, fields[i]); nvlist_move_nvlist(limits, "fields", nvl); return (cap_limit_set(chan, limits)); } int cap_pwd_limit_users(cap_channel_t *chan, const char * const *names, size_t nnames, uid_t *uids, size_t nuids) { nvlist_t *limits, *users; + char nvlname[64]; unsigned int i; + int n; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) { limits = nvlist_create(0); } else { if (nvlist_exists_nvlist(limits, "users")) nvlist_free_nvlist(limits, "users"); } users = nvlist_create(0); - for (i = 0; i < nuids; i++) - nvlist_addf_number(users, (uint64_t)uids[i], "uid%u", i); - for (i = 0; i < nnames; i++) - nvlist_addf_string(users, names[i], "name%u", i); + for (i = 0; i < nuids; i++) { + n = snprintf(nvlname, sizeof(nvlname), "uid%u", i); + assert(n > 0 && n < (int)sizeof(nvlname)); + nvlist_add_number(users, nvlname, (uint64_t)uids[i]); + } + for (i = 0; i < nnames; i++) { + n = snprintf(nvlname, sizeof(nvlname), "name%u", i); + assert(n > 0 && n < (int)sizeof(nvlname)); + nvlist_add_string(users, nvlname, names[i]); + } nvlist_move_nvlist(limits, "users", users); return (cap_limit_set(chan, limits)); }