diff options
Diffstat (limited to 'libshouldbeinlibc')
43 files changed, 4764 insertions, 0 deletions
diff --git a/libshouldbeinlibc/Makefile b/libshouldbeinlibc/Makefile new file mode 100644 index 00000000..14a7939d --- /dev/null +++ b/libshouldbeinlibc/Makefile @@ -0,0 +1,37 @@ +# Makefile for libshouldbeinlibc +# +# Copyright (C) 1995,96,97,98,99,2002,2012 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2, or (at +# your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +dir := libshouldbeinlibc +makemode := library + +libname = libshouldbeinlibc +SRCS = termsize.c timefmt.c exec-reauth.c maptime-funcs.c \ + canon-host.c maptime.c shared-dom.c localhost.c wire.c portinfo.c \ + xportinfo.c portxlate.c lcm.c cacheq.c fsysops.c \ + idvec.c idvec-auth.c idvec-funcs.c \ + idvec-impgids.c idvec-verify.c idvec-rep.c \ + ugids.c ugids-argp.c ugids-rep.c ugids-verify.c ugids-subtract.c \ + ugids-auth.c ugids-xinl.c ugids-merge.c ugids-imply.c ugids-posix.c \ + ugids-verify-auth.c nullauth.c +installhdrs = idvec.h timefmt.h maptime.h \ + wire.h portinfo.h portxlate.h cacheq.h ugids.h nullauth.h +installhdrsubdir = . + +OBJS = $(SRCS:.c=.o) + +include ../Makeconf diff --git a/libshouldbeinlibc/cacheq.c b/libshouldbeinlibc/cacheq.c new file mode 100644 index 00000000..c1be59c0 --- /dev/null +++ b/libshouldbeinlibc/cacheq.c @@ -0,0 +1,143 @@ +/* Helper functions for maintaining a fixed-size lru-ordered queue + + Copyright (C) 1996, 1998 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <string.h> +#include <stdlib.h> + +#include "cacheq.h" + +/* Move ENTRY to the most-recently-used end of CACHEQ. */ +void +cacheq_make_mru (struct cacheq *cq, void *entry) +{ + struct cacheq_hdr *h = entry; + + if (h != cq->mru) + { + /* First remove it. We known H->prev isn't 0 because H wasn't + previously == MRU. */ + ((struct cacheq_hdr *)h->prev)->next = h->next; + if (h->next) + ((struct cacheq_hdr *)h->next)->prev = h->prev; + else + cq->lru = h->prev; + + /* Now make it MRU. */ + h->next = cq->mru; + h->prev = 0; + ((struct cacheq_hdr *)cq->mru)->prev = h; + cq->mru = h; + } +} + +/* Move ENTRY to the least-recently-used end of CACHEQ. */ +void +cacheq_make_lru (struct cacheq *cq, void *entry) +{ + struct cacheq_hdr *h = entry; + + if (h != cq->lru) + { + /* First remove it. We known H->next isn't 0 because H wasn't + previously == LRU. */ + ((struct cacheq_hdr *)h->next)->prev = h->prev; + if (h->prev) + ((struct cacheq_hdr *)h->prev)->next = h->next; + else + cq->mru = h->next; + + /* Now make it LRU. */ + h->prev = cq->lru; + h->next = 0; + ((struct cacheq_hdr *)cq->lru)->next = h; + cq->lru = h; + } +} + +/* Change CQ's size to be LENGTH entries. */ +error_t +cacheq_set_length (struct cacheq *cq, int length) +{ + if (length != cq->length) + { + size_t esz = cq->entry_size; + void *new_entries = malloc (esz * length); + /* Source entries. */ + struct cacheq_hdr *fh = cq->mru; + /* Destination entries (and limit). */ + struct cacheq_hdr *th = new_entries; + struct cacheq_hdr *end = new_entries + esz * (length - 1); + struct cacheq_hdr *prev_th = 0; + + if (! new_entries) + return ENOMEM; + + while (fh || th) + { + struct cacheq_hdr *next_th = + (!th || th >= end) ? 0 : (void *)th + esz; + + if (fh && th) + bcopy (fh, th, esz); /* Copy the bits in a moved entry. */ + else if (th) + bzero (th, esz); /* Zero the bits in a new entry. */ + + if (th) + /* Fixup headers. */ + { + th->prev = prev_th; + th->next = next_th; + } + + /* Call user hooks as appropriate. */ + if (fh && th) + { + if (cq->move_entry) + (*cq->move_entry) (fh, th); + } + else if (th) + { + if (cq->init_entry) + (*cq->init_entry) (th); + } + else + { + if (cq->finalize_entry) + (*cq->finalize_entry) (fh); + } + + if (fh) + fh = fh->next; + if (th) + { + prev_th = th; + th = next_th; + } + } + + free (cq->entries); + cq->entries = new_entries; + cq->mru = new_entries; + cq->lru = prev_th; + cq->length = length; + } + + return 0; +} diff --git a/libshouldbeinlibc/cacheq.h b/libshouldbeinlibc/cacheq.h new file mode 100644 index 00000000..a221a7a7 --- /dev/null +++ b/libshouldbeinlibc/cacheq.h @@ -0,0 +1,87 @@ +/* Helper functions for maintaining a fixed-size lru-ordered queue + + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef __CACHEQ_H__ +#define __CACHEQ_H__ + +#include <stddef.h> +#include <errno.h> + +/* This header occurs at the start of every cacheq entry. */ +struct cacheq_hdr +{ + /* Next and prev entries in the cache, linked in LRU order. These are of + type `void *' so that it's conveient to iterate through the list using a + variable pointing to a structure that contains the header, by using + something like `VAR = VAR->hdr.next'. */ + void *next, *prev; +}; + +/* A cacheq. Note that this structure is laid out to allow convenient use as + static initialized data. */ +struct cacheq +{ + /* The size of each entry, including its cacheq_hdr. */ + size_t entry_size; + + /* If non-0, then when making new entries (for instance, when the cacheq is + initialized, or when its size is increased), this function is called on + each new entry (with it's header already initialized). If this function + isn't defined, then each entry is simply zeroed. */ + void (*init_entry) (void *entry); + + /* When an entry is moved from one place in memory to another (for + instance, changing the size of the cache, new storage is used), this is + called for each entry, with FROM and TO the old and new locations of the + entry (and TO contains a bitwise copy of FROM). This is often useful + when the entry points to something that contains a backpointer to it. */ + void (*move_entry) (void *from, void *to); + + /* When entries are removed for some reason (for instance, when reducing + the size of the cacheq), this function is called on each. */ + void (*finalize_entry) (void *entry); + + /* The number of entries in the cache. This number is fixed. */ + int length; + + /* A buffer holding malloc'd memory for all the entries -- NUM_ENTRIES + entries of size ENTRY_SIZE. */ + void *entries; + + /* The least, and most, recently used entries in the cache. These point to + either end of a linked list composed of all the elements of the cache. + This list will always be the same length -- if an element is `removed', + its entry is simply marked inactive, and moved to the LRU end of the list + so it will be reused first. These pointers are of type `void *' so they + can be conveniently used by client code (see comment in struct + cacheq_hdr). */ + void *lru, *mru; +}; + +/* Move ENTRY to the most-recently-used end of CACHEQ. */ +void cacheq_make_mru (struct cacheq *cq, void *entry); + +/* Move ENTRY to the least-recently-used end of CACHEQ. */ +void cacheq_make_lru (struct cacheq *cq, void *entry); + +/* Change CQ's size to be LENGTH entries. */ +error_t cacheq_set_length (struct cacheq *cq, int length); + +#endif /* __CACHEQ_H__ */ diff --git a/libshouldbeinlibc/canon-host.c b/libshouldbeinlibc/canon-host.c new file mode 100644 index 00000000..41068d30 --- /dev/null +++ b/libshouldbeinlibc/canon-host.c @@ -0,0 +1,57 @@ +/* Host name canonicalization + + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + [This file is from sh-utils/lib; maybe something can be done to share them.] + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <string.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +/* Returns the canonical hostname associated with HOST (allocated in a static + buffer), or 0 if it can't be determined. */ +char * +canon_host (char *host) +{ + struct hostent *he = gethostbyname (host); + + if (he) + { + char *addr = 0; + + /* Try and get an ascii version of the numeric host address. */ + switch (he->h_addrtype) + { + case AF_INET: + addr = inet_ntoa (*(struct in_addr *)he->h_addr); + break; + } + + if (addr && strcmp (he->h_name, addr) == 0) + /* gethostbyname() cheated! Lookup the host name via the address + this time to get the actual host name. */ + he = gethostbyaddr (he->h_addr, he->h_length, he->h_addrtype); + + if (he) + return he->h_name; + } + + return 0; +} diff --git a/libshouldbeinlibc/exec-reauth.c b/libshouldbeinlibc/exec-reauth.c new file mode 100644 index 00000000..263b1408 --- /dev/null +++ b/libshouldbeinlibc/exec-reauth.c @@ -0,0 +1,108 @@ +/* Re-authentication in preparation for an exec + + Copyright (C) 1995, 96, 98, 2000 Free Software Foundation, Inc. + + Stolen by Miles Bader <miles@gnu.ai.mit.edu>, but really + written by Michael I. Bushnell p/BSG <mib@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <mach.h> +#include <hurd/auth.h> +#include <hurd/io.h> +#include <hurd/process.h> + +/* Re-authenticates the ports in PORTS and FDS appropriately (replacing + PORTS[INIT_PORT_AUTH] with AUTH) for a following exec using the auth port + AUTH. Each replaced port has a reference consumed; if an error is + returned, then PORTS and FDS may contain a mixture of old and new ports, + however AUTH will only be placed in PORTS upon success. If SECURE is + true, then it is assumed the exec will use EXEC_SECURE, and certain ports + may be replaced by MACH_PORT_NULL, with the expectation that exec will + fill these in itself; if all ports should be re-authenticated, use 0 for + this argument, regardless of whether EXEC_SECURE will be used. If + MUST_REAUTH is true, then any failure to re-authenticate a port will + result in the function return the error, otherwise, such failures are + silently ignored. */ +error_t +exec_reauth (auth_t auth, int secure, int must_reauth, + mach_port_t *ports, unsigned num_ports, + mach_port_t *fds, unsigned num_fds) +{ + unsigned int i; + error_t err = 0; + + error_t reauth (mach_port_t *port, int isproc) + { + if (*port != MACH_PORT_NULL) + { + mach_port_t newport; + mach_port_t ref = mach_reply_port (); + error_t err = + (isproc ? proc_reauthenticate : io_reauthenticate) + (*port, ref, MACH_MSG_TYPE_MAKE_SEND); + + /* MAKE_SEND is safe here because we destroy REF ourselves. */ + + if (!err) + err = auth_user_authenticate (auth, ref, MACH_MSG_TYPE_MAKE_SEND, + &newport); + mach_port_mod_refs (mach_task_self (), ref, MACH_PORT_RIGHT_RECEIVE, -1); + if (err) + { + if (must_reauth) + return err; + /* Nothing Happens. */ + } + else + { + if (isproc) + mach_port_deallocate (mach_task_self (), newport); + else + { + mach_port_deallocate (mach_task_self (), *port); + *port = newport; + } + } + } + return 0; + } + + /* Re-authenticate all the ports we are handing to the user + with this new port, and install the new auth port in ports. */ + for (i = 0; i < num_fds && !err; ++i) + err = reauth (&fds[i], 0); + + if (!err) + { + if (secure) + /* Not worth doing; the exec server will just do it again. */ + ports[INIT_PORT_CRDIR] = MACH_PORT_NULL; + else + err = reauth (&ports[INIT_PORT_CRDIR], 0); + } + if (!err) + err = reauth (&ports[INIT_PORT_PROC], 1); + if (!err) + err = reauth (&ports[INIT_PORT_CWDIR], 0); + + if (!err) + { + mach_port_deallocate (mach_task_self (), ports[INIT_PORT_AUTH]); + ports[INIT_PORT_AUTH] = auth; + } + + return 0; +} diff --git a/libshouldbeinlibc/fsysops.c b/libshouldbeinlibc/fsysops.c new file mode 100644 index 00000000..dbcae672 --- /dev/null +++ b/libshouldbeinlibc/fsysops.c @@ -0,0 +1,96 @@ +/* Some handy utility routines for fsys control ports + + Copyright (C) 1996, 1999 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <errno.h> +#include <argz.h> +#include <mach.h> +#include <sys/mman.h> +#include <hurd/fsys.h> +#include <string.h> + +/* Make FSYS readonly or writable. */ +error_t +fsys_set_readonly (fsys_t fsys, int readonly) +{ + error_t err; + char *opts = readonly ? "--readonly" : "--writable"; + size_t opts_len = strlen (opts) + 1; + err = fsys_set_options (fsys, opts, opts_len, 0); + if (err == EINVAL) + err = EOPNOTSUPP; + return err; +} + +/* Ask FSYS whether it's readonly, returning the result in READONLY; we don't + really have a good method for this, other than asking for it's options and + looking for `--readonly' or `--writable'. If we see neither, return + EOPNOTSUPP. */ +error_t +fsys_get_readonly (fsys_t fsys, int *readonly) +{ + error_t err; + char _opts[200], *opts = _opts; + size_t opts_len = sizeof opts; + + err = fsys_get_options (fsys, &opts, &opts_len); + if (! err) + { + char *opt; + int ok = 0; + + for (opt = opts + ; !ok && opt && opt < opts + opts_len + ; opt = argz_next (opts, opts_len, opt)) + if (strcasecmp (opt, "--readonly") == 0) + { + *readonly = 1; + ok = 1; + } + else if (strcasecmp (opt, "--writable") == 0) + { + *readonly = 0; + ok = 1; + } + + if (! ok) + err = EOPNOTSUPP; /* So far as we know... */ + + if (opts != _opts) + /* Free out-of-line memory returned by fsys_get_options. */ + munmap (opts, opts_len); + } + + return err; +} + +/* Tell FSYS to remount itself. */ +error_t +fsys_update (fsys_t fsys, int readonly) +{ + error_t err; + char *opts = "--update"; + size_t opts_len = strlen (opts) + 1; + err = fsys_set_options (fsys, opts, opts_len, 0); + if (err == EINVAL) + err = EOPNOTSUPP; + return err; +} diff --git a/libshouldbeinlibc/idvec-auth.c b/libshouldbeinlibc/idvec-auth.c new file mode 100644 index 00000000..1858bd6a --- /dev/null +++ b/libshouldbeinlibc/idvec-auth.c @@ -0,0 +1,85 @@ +/* Idvec functions that interact with an auth server + + Copyright (C) 1995, 1998, 1999, 2001, 2002, 2008 + Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <mach.h> +#include <sys/mman.h> +#include <hurd/auth.h> +#include <errno.h> + +#include "idvec.h" + +/* Add to all of EFF_UIDS, AVAIL_UIDS, EFF_GIDS, AVAIL_GIDS (as if with + idvec_merge_ids()) the ids associated with the auth port AUTH. Any of + these parameters may be NULL if that information isn't desired. */ +error_t +idvec_merge_auth (struct idvec *eff_uids, struct idvec *avail_uids, + struct idvec *eff_gids, struct idvec *avail_gids, + auth_t auth) +{ + error_t err; + uid_t eff_uid_buf[10], avail_uid_buf[20]; + uid_t *_eff_uids = eff_uid_buf, *_avail_uids = avail_uid_buf; + size_t num_eff_uids = 10, num_avail_uids = 20; + uid_t eff_gid_buf[10], avail_gid_buf[20]; + uid_t *_eff_gids = eff_gid_buf, *_avail_gids = avail_gid_buf; + size_t num_eff_gids = 10, num_avail_gids = 20; + + err = auth_getids (auth, + &_eff_uids, &num_eff_uids, &_avail_uids, &num_avail_uids, + &_eff_gids, &num_eff_gids, &_avail_gids, &num_avail_gids); + if (err) + return err; + + if (eff_uids) + err = idvec_grow (eff_uids, num_eff_uids); + if (avail_uids && !err) + err = idvec_grow (avail_uids, num_avail_uids); + if (eff_gids && !err) + err = idvec_grow (eff_gids, num_eff_gids); + if (avail_gids && !err) + err = idvec_grow (avail_gids, num_avail_gids); + + if (!err) + /* Now that we've ensured there's enough space, none of these should + return an error. */ + { + if (eff_uids) + idvec_merge_ids (eff_uids, _eff_uids, num_eff_uids); + if (avail_uids) + idvec_merge_ids (avail_uids, _avail_uids, num_avail_uids); + if (eff_gids) + idvec_merge_ids (eff_gids, _eff_gids, num_eff_gids); + if (avail_gids) + idvec_merge_ids (avail_gids, _avail_gids, num_avail_gids); + } + + /* Deallocate any out-of-line memory we got back. */ + if (_eff_uids != eff_uid_buf) + munmap ((caddr_t) _eff_uids, num_eff_uids * sizeof (uid_t)); + if (_avail_uids != avail_uid_buf) + munmap ((caddr_t) _avail_uids, num_avail_uids * sizeof (uid_t)); + if (_eff_gids != eff_gid_buf) + munmap ((caddr_t) _eff_gids, num_eff_gids * sizeof (gid_t)); + if (_avail_gids != avail_gid_buf) + munmap ((caddr_t) _avail_gids, num_avail_gids * sizeof (gid_t)); + + return err; +} diff --git a/libshouldbeinlibc/idvec-funcs.c b/libshouldbeinlibc/idvec-funcs.c new file mode 100644 index 00000000..3bb0318d --- /dev/null +++ b/libshouldbeinlibc/idvec-funcs.c @@ -0,0 +1,2 @@ +#define IDVEC_DEFINE_EI +#include "idvec.h" diff --git a/libshouldbeinlibc/idvec-impgids.c b/libshouldbeinlibc/idvec-impgids.c new file mode 100644 index 00000000..d89f4873 --- /dev/null +++ b/libshouldbeinlibc/idvec-impgids.c @@ -0,0 +1,115 @@ +/* Add gids implied by a user + + Copyright (C) 1997, 2001, 2014 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdlib.h> +#include <errno.h> +#include <idvec.h> +#include <pwd.h> +#include <grp.h> + +#define NUM_STATIC_GIDS 100 /* Initial size of static gid array. */ + +/* The set of gids implied by a uid. */ +struct uid_implies +{ + uid_t uid; /* this uid... */ + struct idvec *implies; /* implies these gids. */ + struct uid_implies *next; +}; + +/* Cache of previously calculated results for add_implied_gids. */ +static struct uid_implies *uid_implies_cache = 0; + +/* Add to IMPLIED_GIDS those group ids implied by the user UID. */ +static error_t +_merge_implied_gids (struct idvec *implied_gids, uid_t uid) +{ + struct uid_implies *ui; + + for (ui = uid_implies_cache; ui; ui = ui->next) + if (ui->uid == uid) + return idvec_merge (implied_gids, ui->implies); + + { + error_t err = 0; + struct passwd *pw = getpwuid (uid); + + if (! pw) + err = EINVAL; + else + { + struct idvec *cache = make_idvec (); + gid_t _gids[NUM_STATIC_GIDS], *gids = _gids; + int maxgids = NUM_STATIC_GIDS; + int ngids = getgrouplist (pw->pw_name, pw->pw_gid, gids, &maxgids); + + if (ngids == -1) + { + gids = malloc (maxgids * sizeof (gid_t)); + if (! gids) + err = ENOMEM; + else + ngids = getgrouplist (pw->pw_name, pw->pw_gid, gids, &maxgids); + } + + if (! cache) + err = ENOMEM; + + if (! err) + { + err = idvec_merge_ids (cache, gids, ngids); + if (gids != _gids) + free (gids); + } + + if (! err) + { + idvec_merge (implied_gids, cache); + ui = malloc (sizeof (struct uid_implies)); + if (ui) + { + ui->uid = uid; + ui->implies = cache; + ui->next = uid_implies_cache; + uid_implies_cache = ui; + } + else + idvec_free (cache); + } + } + + return err; + } +} + +/* Add to GIDS those group ids implied by the users in UIDS. */ +error_t +idvec_merge_implied_gids (struct idvec *gids, const struct idvec *uids) +{ + unsigned int i; + error_t err = 0; + for (i = 0; i < uids->num; i++) + { + error_t this_err = _merge_implied_gids (gids, uids->ids[i]); + if (this_err && !err) + err = this_err; + } + return err; +} diff --git a/libshouldbeinlibc/idvec-rep.c b/libshouldbeinlibc/idvec-rep.c new file mode 100644 index 00000000..16408a4d --- /dev/null +++ b/libshouldbeinlibc/idvec-rep.c @@ -0,0 +1,164 @@ +/* idvec string representation + + Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <idvec.h> +#include <grp.h> +#include <pwd.h> + +/* Return a string representation of the ids in IDVEC, each id separated by + the string SEP (default ","). SHOW_VALUES and SHOW_NAMES reflect how each + id is printed (if SHOW_NAMES is true values are used where names aren't + available); if both are true, the `VALUE(NAME)' format is used. + ID_NAME_FN is used to map each id to a name; it should return a malloced + string, which will be freed here. The empty string is returned for an + empty list, and 0 for an allocation error. */ +char * +idvec_rep (const struct idvec *idvec, int show_values, int show_names, + char *(*id_name_fn) (uid_t id), const char *sep) +{ + size_t sep_len; + char *rep = 0; + size_t rep_len = 0, rep_sz = 0; + + int ensure_room (size_t amount) + { + size_t end = rep_len + amount; + if (end > rep_sz) + { + size_t new_sz = rep_sz + end; + char *new_rep = realloc (rep, new_sz); + if (new_rep) + { + rep = new_rep; + rep_sz = new_sz; + } + else + return 0; + } + return 1; + } + int add_id (uid_t val, char *name) + { + if (!name || show_values) + { + if (! ensure_room (10)) + return 0; + rep_len += snprintf (rep + rep_len, 10, "%d", val); + } + if (name) + { + size_t nlen = strlen (name) + 3; + if (! ensure_room (nlen)) + { + free (name); + return 0; + } + rep_len += + snprintf (rep + rep_len, nlen, show_values ? "(%s)" : "%s", name); + free (name); + } + return 1; + } + + if (! sep) + sep = ","; + sep_len = strlen (sep); + + if (idvec->num > 0) + { + unsigned int i; + + for (i = 0; i < idvec->num; i++) + { + char *name = 0; + uid_t val = idvec->ids[i]; + + if (i > 0) + { + if (ensure_room (sep_len)) + { + strcpy (rep + rep_len, sep); + rep_len += sep_len; + } + else + break; + } + + if (show_names || !show_values) + name = (*id_name_fn) (val); + if (! add_id (val, name)) + break; + } + + if (i < idvec->num) + { + free (rep); + return 0; + } + + return rep; + } + + return strdup (""); +} + +/* Return a malloced string with the name of the user UID. */ +static char * +lookup_uid (uid_t uid) +{ + char buf[1024]; + struct passwd _pw, *pw; + if (getpwuid_r (uid, &_pw, buf, sizeof buf, &pw) == 0) + return strdup (pw->pw_name); + else + return 0; +} + +/* Return a malloced string with the name of the group GID. */ +static char * +lookup_gid (gid_t gid) +{ + char buf[1024]; + struct group _gr, *gr; + if (getgrgid_r (gid, &_gr, buf, sizeof buf, &gr) == 0) + return strdup (gr->gr_name); + else + return 0; +} + +/* Like idvec_rep, mapping ids to user names. */ +char * +idvec_uids_rep (const struct idvec *idvec, int show_values, int show_names, + const char *sep) +{ + return idvec_rep (idvec, show_values, show_names, lookup_uid, sep); +} + +/* Like idvec_rep, mapping ids to group names. */ +char * +idvec_gids_rep (const struct idvec *idvec, int show_values, int show_names, + const char *sep) +{ + return idvec_rep (idvec, show_values, show_names, lookup_gid, sep); +} diff --git a/libshouldbeinlibc/idvec-verify.c b/libshouldbeinlibc/idvec-verify.c new file mode 100644 index 00000000..4d9b6dbe --- /dev/null +++ b/libshouldbeinlibc/idvec-verify.c @@ -0,0 +1,362 @@ +/* Verify user passwords + + Copyright (C) 1996, 1997, 1998, 1999, 2002, 2008 + Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <idvec.h> +#include <grp.h> +#include <pwd.h> +#include <shadow.h> +#include <crypt.h> + +#define SHADOW_PASSWORD_STRING "x" /* pw_passwd contents for shadow passwd */ + +#pragma weak crypt + +static error_t verify_id (); /* FWD */ + +/* Get a password from the user, returning it in malloced storage. */ +static char * +get_passwd (const char *prompt, + uid_t id, int is_group, + void *pwd_or_grp, void *hook) +{ + char *st = getpass (prompt); + if (st) + st = strdup (st); + return st; +} + +/* Verify PASSWORD using /etc/passwd (and maybe /etc/shadow). */ +static error_t +verify_passwd (const char *password, + uid_t id, int is_group, + void *pwd_or_grp, void *hook) +{ + const char *encrypted; + int wheel_uid = (intptr_t)hook; + const char *sys_encrypted; + + if (! pwd_or_grp) + /* No password db entry for ID; if ID is root, the system is probably + really fucked up, so grant it (heh). */ + return (id == 0 ? 0 : EACCES); + + /* The encrypted password in the passwd db. */ + sys_encrypted = + (is_group + ? ((struct passwd *)pwd_or_grp)->pw_passwd + : ((struct group *)pwd_or_grp)->gr_passwd); + + if (sys_encrypted[0] == '\0') + return 0; /* No password. */ + + if (crypt) + /* Encrypt the password entered by the user (SYS_ENCRYPTED is the salt). */ + encrypted = crypt (password, sys_encrypted); + else + /* No crypt on this system! Use plain-text passwords. */ + encrypted = password; + + if (! encrypted) + /* Crypt failed. */ + return errno; + + /* See whether the user's password matches the system one. */ + if (strcmp (encrypted, sys_encrypted) == 0) + /* Password check succeeded. */ + return 0; + else if (id == 0 && !is_group && wheel_uid) + /* Special hack: a user attempting to gain root access can use + their own password (instead of root's) if they're in group 0. */ + { + struct passwd _pw, *pw; + char lookup_buf[1024]; + char sp_lookup_buf[1024]; + + const char *check_shadow (struct passwd *pw) + { + if (strcmp (pw->pw_passwd, SHADOW_PASSWORD_STRING) == 0) + { + /* When encrypted password is "x", try shadow passwords. */ + struct spwd _sp, *sp; + if (getspnam_r (pw->pw_name, &_sp, sp_lookup_buf, + sizeof sp_lookup_buf, &sp) == 0) + return sp->sp_pwdp; + } + return pw->pw_passwd; + } + + if (getpwuid_r (wheel_uid, &_pw, lookup_buf, sizeof lookup_buf, &pw)) + return errno ?: EINVAL; + + sys_encrypted = check_shadow (pw); + + encrypted = crypt (password, sys_encrypted); + if (! encrypted) + /* Crypt failed. */ + return errno; + + if (strcmp (encrypted, sys_encrypted) == 0) + /* *this* password is correct! */ + return 0; + } + + return EACCES; +} + +/* Make sure the user has the right to the ids in UIDS and GIDS, given that + we know he already has HAVE_UIDS and HAVE_GIDS, asking for passwords (with + GETPASS_FN) where necessary; any of the arguments may be 0, which is + treated the same as if they were empty. 0 is returned if access should be + allowed, otherwise EINVAL if an incorrect password was entered, or an + error relating to resource failure. Any uid/gid < 0 will be guaranteed to + fail regardless of what the user types. GETPASS_FN should ask for a + password from the user, and return it in malloced storage; it defaults to + using the standard libc function getpass. If VERIFY_FN is 0, then the + users password will be encrypted with crypt and compared with the + password/group entry's encrypted password, otherwise, VERIFY_FN will be + called to check the entered password's validity; it should return 0 if the + given password is correct, or an error code. The common arguments to + GETPASS_FN and VERIFY_FN are: ID, the user/group id; IS_GROUP, true if its + a group, or false if a user; PWD_OR_GRP, a pointer to either the passwd or + group entry for ID, and HOOK, containing the appropriate hook passed into + idvec_verify. */ +error_t +idvec_verify (const struct idvec *uids, const struct idvec *gids, + const struct idvec *have_uids, const struct idvec *have_gids, + char *(*getpass_fn) (const char *prompt, + uid_t id, int is_group, + void *pwd_or_grp, void *hook), + void *getpass_hook, + error_t (*verify_fn) (const char *password, + uid_t id, int is_group, + void *pwd_or_grp, void *hook), + void *verify_hook) +{ + if (have_uids && idvec_contains (have_uids, 0)) + /* Root can do anything. */ + return 0; + else + { + unsigned int i; + int multiple = 0; /* Asking for multiple ids? */ + error_t err = 0; /* Our return status. */ + struct idvec implied_gids = IDVEC_INIT; /* Gids implied by uids. */ + /* If we already are in group 0 (`wheel'), this user's password can be + used to get root privileges instead of root's. */ + int wheel_uid = + ((have_uids && have_gids + && (idvec_contains (have_gids, 0) && have_uids->num > 0)) + ? have_uids->ids[0] + : 0); + + if (! verify_fn) + { + verify_fn = verify_passwd; + verify_hook = (void *)(intptr_t)wheel_uid; + } + + /* See if there are multiple ids in contention, in which case we should + name each user/group as we ask for its password. */ + if (uids && gids) + { + int num_non_implied_gids = 0; + + /* Calculate which groups we need not ask about because they are + implied by the uids which we (will) have verified. Note that we + ignore any errors; at most, it means we will ask for too many + passwords. */ + idvec_merge_implied_gids (&implied_gids, uids); + + for (i = 0; i < gids->num; i++) + if (! idvec_contains (&implied_gids, gids->ids[i])) + num_non_implied_gids++; + + multiple = (uids->num + num_non_implied_gids) > 1; + } + else if (uids) + multiple = uids->num > 1; + else if (gids) + multiple = gids->num > 1; + + if (uids && idvec_contains (uids, 0)) + /* root is being asked for, which, once granted will provide access for + all the others. */ + err = verify_id (0, 0, multiple, + getpass_fn, getpass_hook, verify_fn, verify_hook); + else + { + if (uids) + /* Check uids */ + for (i = 0; i < uids->num && !err; i++) + { + uid_t uid = uids->ids[i]; + if (!have_uids || !idvec_contains (have_uids, uid)) + err = verify_id (uid, 0, multiple, + getpass_fn, getpass_hook, verify_fn, verify_hook); + } + + if (gids) + /* Check gids */ + for (i = 0; i < gids->num && !err; i++) + { + gid_t gid = gids->ids[i]; + if ((!have_gids || !idvec_contains (have_gids, gid)) + && !idvec_contains (&implied_gids, gid)) + err = verify_id (gid, 1, multiple, + getpass_fn, getpass_hook, verify_fn, verify_hook); + } + } + + idvec_fini (&implied_gids); + + return err; + } +} + +/* Verify that the user should be allowed to assume the indentity of the + user/group ID (depending on whether IS_GROUP is false/true). If MULTIPLE + is true, then this is one of multiple ids being verified, so */ +static error_t +verify_id (uid_t id, int is_group, int multiple, + char *(*getpass_fn) (const char *prompt, + uid_t id, int is_group, + void *pwd_or_grp, void *hook), + void *getpass_hook, + error_t (*verify_fn) (const char *password, + uid_t id, int is_group, + void *pwd_or_grp, void *hook), + void *verify_hook) +{ + int err; + void *pwd_or_grp = 0; + char *name = 0; + char *prompt = 0, *password; + char id_lookup_buf[1024]; + char sp_lookup_buf[1024]; + + /* VERIFY_FN should have been defaulted in idvec_verify if necessary. */ + assert (verify_fn); + + if (id != (uid_t) -1) + do + { + if (is_group) + { + struct group _gr, *gr; + if (getgrgid_r (id, &_gr, id_lookup_buf, sizeof id_lookup_buf, &gr) + == 0) + { + if (!gr->gr_passwd || !*gr->gr_passwd) + return (*verify_fn) ("", id, 1, gr, verify_hook); + name = gr->gr_name; + pwd_or_grp = gr; + } + } + else + { + struct passwd _pw, *pw; + if (getpwuid_r (id, &_pw, id_lookup_buf, sizeof id_lookup_buf, &pw) + == 0) + { + if (strcmp (pw->pw_passwd, SHADOW_PASSWORD_STRING) == 0) + { + /* When encrypted password is "x", check shadow + passwords to see if there is an empty password. */ + struct spwd _sp, *sp; + if (getspnam_r (pw->pw_name, &_sp, sp_lookup_buf, + sizeof sp_lookup_buf, &sp) == 0) + /* The storage for the password string is in + SP_LOOKUP_BUF, a local variable in this function. + We Know that the only use of PW->pw_passwd will be + in the VERIFY_FN call in this function, and that + the pointer will not be stored past the call. */ + pw->pw_passwd = sp->sp_pwdp; + } + + if (pw->pw_passwd[0] == '\0') + return (*verify_fn) ("", id, 0, pw, verify_hook); + name = pw->pw_name; + pwd_or_grp = pw; + } + } + if (! name) + { + /* [ug]id lookup failed! */ + if (id != 0 || is_group) + /* If ID != 0, then it's probably just an unknown id, so ask for + the root password instead -- root should be able to do + anything. */ + { + id = 0; /* Root */ + is_group = 0; /* uid */ + multiple = 1; /* Explicitly ask for root's password. */ + } + else + /* No password entry for root. */ + name = "root"; + } + } + while (! name); + + if (! getpass_fn) + /* Default GETPASS_FN to using getpass. */ + getpass_fn = get_passwd; + + if (multiple) + { + if (name) + asprintf (&prompt, "Password for %s%s:", + is_group ? "group " : "", name); + else + asprintf (&prompt, "Password for %s %d:", + is_group ? "group" : "user", id); + } + + /* Prompt the user for the password. */ + if (prompt) + { + password = + (*getpass_fn) (prompt, id, is_group, pwd_or_grp, getpass_hook); + free (prompt); + } + else + password = + (*getpass_fn) ("Password:", id, is_group, pwd_or_grp, getpass_hook); + + /* Check the user's answer. */ + if (password) + { + err = (*verify_fn) (password, id, is_group, pwd_or_grp, verify_hook); + + /* Paranoia may destroya. */ + memset (password, 0, strlen (password)); + + free (password); + } + else + err = EACCES; + + return err; +} diff --git a/libshouldbeinlibc/idvec.c b/libshouldbeinlibc/idvec.c new file mode 100644 index 00000000..7fdee104 --- /dev/null +++ b/libshouldbeinlibc/idvec.c @@ -0,0 +1,339 @@ +/* Routines for vectors of uids/gids + + Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <malloc.h> +#include <string.h> + +#include "idvec.h" + +/* Return a new, empty, idvec, or NULL if there wasn't enough memory. */ +struct idvec * +make_idvec () +{ + struct idvec *idvec = malloc (sizeof (struct idvec)); + if (idvec) + { + idvec->alloced = idvec->num = 0; + idvec->ids = 0; + } + return idvec; +} + +/* Free's IDVEC, but not the storage pointed to by the IDS field. */ +void +idvec_free_wrapper (struct idvec *idvec) +{ + free (idvec); +} + +void +idvec_free_contents (struct idvec *idvec) +{ + if (idvec->alloced) + free (idvec->ids); +} + +void +idvec_free (struct idvec *idvec) +{ + idvec_free_contents (idvec); + idvec_free_wrapper (idvec); +} + +/* Ensure that IDVEC has enough spaced allocated to hold NUM ids, thus + ensuring that any subsequent ids added won't return a memory allocation + error unless it would result in more ids that NUM. ENOMEM is returned if + a memory allocation error occurs. */ +error_t +idvec_ensure (struct idvec *idvec, unsigned num) +{ + if (num > idvec->alloced) + { + uid_t *_ids = realloc (idvec->ids, num * sizeof (uid_t)); + if (! _ids) + return ENOMEM; + idvec->ids = _ids; + idvec->alloced = num; + } + return 0; +} + +/* Like idvec_ensure(), but takes INC, the increment of the number of ids + already in IDVEC as an argument. */ +error_t +idvec_grow (struct idvec *idvec, unsigned inc) +{ + return idvec_ensure (idvec, idvec->num + inc); +} + +/* Returns true if IDVEC contains ID, at or after position POS. */ +int +idvec_tail_contains (const struct idvec *idvec, unsigned pos, uid_t id) +{ + uid_t *ids = idvec->ids, *end = ids + idvec->num, *p = ids + pos; + while (p < end) + if (*p++ == id) + return 1; + return 0; +} + +/* Insert ID into IDVEC at position POS, returning ENOMEM if there wasn't + enough memory, or 0. */ +error_t +idvec_insert (struct idvec *idvec, unsigned pos, uid_t id) +{ + error_t err = 0; + unsigned num = idvec->num; + unsigned new_num = (pos < num ? num + 1 : pos + 1); + + if (idvec->alloced == num) + /* If we seem to be growing by just one, actually prealloc some more. */ + err = idvec_ensure (idvec, new_num + num); + else + err = idvec_ensure (idvec, new_num); + + if (! err) + { + uid_t *ids = idvec->ids; + if (pos < num) + bcopy (ids + pos, ids + pos + 1, (num - pos) * sizeof (uid_t)); + else if (pos > num) + bzero (ids + num, (pos - num) * sizeof (uid_t)); + ids[pos] = id; + idvec->num = new_num; + } + + return err; +} + +/* Add ID onto the end of IDVEC, returning ENOMEM if there's not enough memory, + or 0. */ +error_t +idvec_add (struct idvec *idvec, uid_t id) +{ + return idvec_insert (idvec, idvec->num, id); +} + +/* If IDVEC doesn't contain ID, add it onto the end, returning ENOMEM if + there's not enough memory; otherwise, do nothing. */ +error_t +idvec_add_new (struct idvec *idvec, uid_t id) +{ + if (idvec_contains (idvec, id)) + return 0; + else + return idvec_add (idvec, id); +} + +/* If IDVEC doesn't contain ID at position POS or after, insert it at POS, + returning ENOMEM if there's not enough memory; otherwise, do nothing. */ +error_t +idvec_insert_new (struct idvec *idvec, unsigned pos, uid_t id) +{ + if (idvec_tail_contains (idvec, pos, id)) + return 0; + else + return idvec_insert (idvec, pos, id); +} + +/* Set the ids in IDVEC to IDS (NUM elements long); delete whatever + the previous ids were. */ +error_t +idvec_set_ids (struct idvec *idvec, const uid_t *ids, unsigned num) +{ + error_t err; + + err = idvec_ensure (idvec, num); + if (!err) + { + bcopy (ids, idvec->ids, num * sizeof (uid_t)); + idvec->num = num; + } + return err; +} + +/* Like idvec_set_ids, but get the new ids from new. */ +error_t +idvec_set (struct idvec *idvec, const struct idvec *new) +{ + return idvec_set_ids (idvec, new->ids, new->num); +} + +/* Adds each id in the vector IDS (NUM elements long) to IDVEC, as long as it + wasn't previously in IDVEC. */ +error_t +idvec_merge_ids (struct idvec *idvec, const uid_t *ids, unsigned num) +{ + error_t err = 0; + unsigned num_old = idvec->num; + while (num-- > 0 && !err) + { + unsigned int i; + for (i = 0; i < num_old; i++) + if (idvec->ids[i] == *ids) + break; + if (i == num_old) + err = idvec_add (idvec, *ids); + ids++; + } + return err; +} + +/* Adds each id from NEW to IDVEC, as if with idvec_add_new(). */ +error_t +idvec_merge (struct idvec *idvec, const struct idvec *new) +{ + return idvec_merge_ids (idvec, new->ids, new->num); +} + +/* Remove any occurrences of ID in IDVEC after position POS. + Returns true if anything was done. */ +int +idvec_remove (struct idvec *idvec, unsigned pos, uid_t id) +{ + if (pos < idvec->num) + { + int left = idvec->num - pos; + uid_t *ids = idvec->ids + pos, *targ = ids; + while (left--) + { + if (*ids != id) + { + if (ids != targ) + *targ = *ids; + targ++; + } + ids++; + } + if (ids == targ) + return 0; + idvec->num = targ - idvec->ids; + return 1; + } + else + return 0; +} + +/* Remove all ids in SUB from IDVEC, returning true if anything was done. */ +int +idvec_subtract (struct idvec *idvec, const struct idvec *sub) +{ + unsigned int i; + int done = 0; + for (i = 0; i < sub->num; i++) + done |= idvec_remove (idvec, 0, sub->ids[i]); + return done; +} + +/* Remove all ids from IDVEC that are *not* in KEEP, returning true if + anything was changed. */ +int +idvec_keep (struct idvec *idvec, const struct idvec *keep) +{ + uid_t *old = idvec->ids, *new = old, *end = old + idvec->num; + + while (old < end) + { + uid_t id = *old++; + if (idvec_contains (keep, id)) + { + if (old != new) + *new = id; + new++; + } + } + + if (old != new) + { + idvec->num = new - idvec->ids; + return 1; + } + else + return 0; +} + +/* Deleted the id at position POS in IDVEC. */ +void +idvec_delete (struct idvec *idvec, unsigned pos) +{ + unsigned num = idvec->num; + if (pos < num) + { + uid_t *ids = idvec->ids; + idvec->num = --num; + if (num > pos) + bcopy (ids + pos + 1, ids + pos, (num - pos) * sizeof (uid_t)); + } +} + +/* Insert ID at position POS in IDVEC, remove any instances of ID previously + present at POS or after. ENOMEM is returned if there's not enough memory, + otherwise 0. */ +error_t +idvec_insert_only (struct idvec *idvec, unsigned pos, uid_t id) +{ + if (idvec->num > pos && idvec->ids[pos] == id) + return 0; + else + { + idvec_remove (idvec, pos, id); + return idvec_insert (idvec, pos, id); + } +} + +/* EFF and AVAIL should be idvec's corresponding to a processes + effective and available ids. ID replaces the first id in EFF, and, + if there are any IDs in AVAIL, replaces the second ID in AVAIL; + what it replaces in any case is preserved by adding it to AVAIL if + not already present. In addition, the If SECURE is non-NULL, and + ID was not previously present in either EFF or AVAIL, then *SECURE + is set to true. ENOMEM is returned if a malloc fails, otherwise 0. + The return parameters are only touched if this call succeeds. */ +error_t +idvec_setid (struct idvec *eff, struct idvec *avail, uid_t id, int *secure) +{ + error_t err; + /* True if ID was not previously present in either EFF or AVAIL. */ + int _secure = !idvec_contains (eff, id) && !idvec_contains (avail, id); + + if (eff->num > 0) + { + /* If there are any old effective ids, we replace eff[0] with + ID, and try to preserve the old eff[0] by putting it in AVAIL + list if necessary. */ + err = idvec_add_new (avail, eff->ids[0]); + if (!err) + eff->ids[0] = id; + } + else + /* No previous effective ids, just make ID the first one. */ + err = idvec_add (eff, id); + + if (avail->num > 0 && !err) + err = idvec_insert_only (avail, 1, id); + + if (err) + return err; + + if (_secure && secure && !*secure) + *secure = 1; + + return 0; +} diff --git a/libshouldbeinlibc/idvec.h b/libshouldbeinlibc/idvec.h new file mode 100644 index 00000000..d6ec1553 --- /dev/null +++ b/libshouldbeinlibc/idvec.h @@ -0,0 +1,236 @@ +/* Routines for vectors of uids/gids + + Copyright (C) 1995,96,97,99,2001 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef __IDVEC_H__ +#define __IDVEC_H__ + +#include <sys/types.h> +#include <hurd/hurd_types.h> +#include <string.h> +#include <features.h> + +#ifdef IDVEC_DEFINE_EI +#define IDVEC_EI +#else +#define IDVEC_EI __extern_inline +#endif + +struct idvec +{ + uid_t *ids; + unsigned num, alloced; +}; + +#define IDVEC_INIT { 0 } + +/* Return a new, empty, idvec, or NULL if there wasn't enough memory. */ +struct idvec *make_idvec (void); + +/* Free the storage pointed to by IDVEC->ids. */ +void idvec_free_contents (struct idvec *idvec); +#define idvec_fini idvec_free_contents + +/* Free IDVEC, but not the storage pointed to by the IDS field. */ +void idvec_free_wrapper (struct idvec *idvec); + +/* Free IDVEC and any storage associated with it. */ +void idvec_free (struct idvec *idvec); + +extern void idvec_clear (struct idvec *idvec); + +extern int idvec_is_empty (const struct idvec *idvec); + +extern int idvec_equal (const struct idvec *idvec1, const struct idvec *idvec2); + +#if defined(__USE_EXTERN_INLINES) || defined(IDVEC_DEFINE_EI) + +/* Mark IDVEC as not containing any ids. */ +IDVEC_EI void +idvec_clear (struct idvec *idvec) +{ + idvec->num = 0; +} + +/* Returns true if IDVEC contains no ids. */ +IDVEC_EI int +idvec_is_empty (const struct idvec *idvec) +{ + return idvec->num == 0; +} + +/* Return true if IDVEC1 has contents identical to IDVEC2. */ +IDVEC_EI int +idvec_equal (const struct idvec *idvec1, const struct idvec *idvec2) +{ + size_t num = idvec1->num; + return idvec2->num == num + && (num == 0 + || memcmp (idvec1->ids, idvec2->ids, num * sizeof *idvec1->ids) == 0); +} + +#endif /* Use extern inlines. */ + +/* Ensure that IDVEC has enough spaced allocated to hold NUM ids, thus + ensuring that any subsequent ids added won't return a memory allocation + error unless it would result in more ids that NUM. ENOMEM is returned if + a memory allocation error occurs. */ +error_t idvec_ensure (struct idvec *idvec, unsigned num); + +/* Like idvec_ensure(), but takes INC, the increment of the number of ids + already in IDVEC as an argument. */ +error_t idvec_grow (struct idvec *idvec, unsigned inc); + +/* Returns true if IDVEC contains ID, at or after position POS. */ +int idvec_tail_contains (const struct idvec *idvec, unsigned pos, uid_t id); + +extern int idvec_contains (const struct idvec *idvec, uid_t id); + +#if defined(__USE_EXTERN_INLINES) || defined(IDVEC_DEFINE_EI) + +/* Returns true if IDVEC contains ID. */ +IDVEC_EI int +idvec_contains (const struct idvec *idvec, uid_t id) +{ + return idvec_tail_contains (idvec, 0, id); +} + +#endif /* Use extern inlines. */ + +/* Insert ID into IDVEC at position POS, returning ENOMEM if there wasn't + enough memory, or 0. */ +error_t idvec_insert (struct idvec *idvec, unsigned pos, uid_t id); + +/* Add ID onto the end of IDVEC, returning ENOMEM if there's not enough memory, + or 0. */ +error_t idvec_add (struct idvec *idvec, uid_t id); + +/* If IDVEC doesn't contain ID, add it onto the end, returning ENOMEM if + there's not enough memory; otherwise, do nothing. */ +error_t idvec_add_new (struct idvec *idvec, uid_t id); + +/* If IDVEC doesn't contain ID at position POS or after, insert it at POS, + returning ENOMEM if there's not enough memory; otherwise, do nothing. */ +error_t idvec_insert_new (struct idvec *idvec, unsigned pos, uid_t id); + +/* Set the ids in IDVEC to IDS (NUM elements long); delete whatever + the previous ids were. */ +error_t idvec_set_ids (struct idvec *idvec, const uid_t *ids, unsigned num); + +/* Like idvec_set_ids, but get the new ids from new. */ +error_t idvec_set (struct idvec *idvec, const struct idvec *new); + +/* Adds each id in the vector IDS (NUM elements long) to IDVEC, as if with + idvec_add_new(). */ +error_t idvec_merge_ids (struct idvec *idvec, const uid_t *ids, unsigned num); + +/* Adds each id from NEW to IDVEC, as if with idvec_add_new(). */ +error_t idvec_merge (struct idvec *idvec, const struct idvec *new); + +/* Remove all ids in SUB from IDVEC, returning true if anything was done. */ +int idvec_subtract (struct idvec *idvec, const struct idvec *sub); + +/* Remove all ids from IDVEC that are *not* in KEEP, returning true if + anything was changed. */ +int idvec_keep (struct idvec *idvec, const struct idvec *keep); + +/* Remove any occurrences of ID in IDVEC after position POS> Returns true if + anything was done. */ +int idvec_remove (struct idvec *idvec, unsigned pos, uid_t id); + +/* Deleted the id at position POS in IDVEC. */ +void idvec_delete (struct idvec *idvec, unsigned pos); + +/* Insert ID at position POS in IDVEC, remove any instances of ID previously + present at POS or after. ENOMEM is returned if there's not enough memory, + otherwise 0. */ +error_t idvec_insert_only (struct idvec *idvec, unsigned pos, uid_t id); + +/* EFF and AVAIL should be idvec's corresponding to a process's + effective and available ids. ID replaces the first id in EFF, and, + if there are any IDs in AVAIL, replaces the second ID in AVAIL; + what it replaces in any case is preserved by adding it to AVAIL if + not already present. In addition, the If SECURE is non-NULL, and + ID was not previously present in either EFF or AVAIL, then *SECURE + is set to true. ENOMEM is returned if a malloc fails, otherwise 0. + The return parameters are only touched if this call succeeds. */ +error_t idvec_setid (struct idvec *eff, struct idvec *avail, uid_t id, + int *secure); + +/* Add to all of EFF_UIDS, AVAIL_UIDS, EFF_GIDS, AVAIL_GIDS (as if with + idvec_merge) the ids associated with the auth port AUTH. Any of these + parameters may be NULL if that information isn't desired. */ +error_t idvec_merge_auth (struct idvec *eff_uids, struct idvec *avail_uids, + struct idvec *eff_gids, struct idvec *avail_gids, + auth_t auth); + +/* Add to GIDS those group ids implied by the users in UIDS. */ +error_t idvec_merge_implied_gids (struct idvec *gids, const struct idvec *uids); + +/* Make sure the user has the right to the ids in UIDS and GIDS, given that + we know he already has HAVE_UIDS and HAVE_GIDS, asking for passwords (with + GETPASS_FN) where necessary; any of the arguments may be 0, which is + treated the same as if they were empty. 0 is returned if access should be + allowed, otherwise EINVAL if an incorrect password was entered, or an + error relating to resource failure. Any uid/gid < 0 will be guaranteed to + fail regardless of what the user types. GETPASS_FN should ask for a + password from the user, and return it in malloced storage; it defaults to + using the standard libc function getpass. If VERIFY_FN is 0, then the + users password will be encrypted with crypt and compared with the + password/group entry's encrypted password, otherwise, VERIFY_FN will be + called to check the entered password's validity; it should return 0 if the + given password is correct, or an error code. The common arguments to + GETPASS_FN and VERIFY_FN are: ID, the user/group id; IS_GROUP, true if its + a group, or false if a user; PWD_OR_GRP, a pointer to either the passwd or + group entry for ID, and HOOK, containing the appropriate hook passed into + idvec_verify. */ +error_t idvec_verify (const struct idvec *uids, const struct idvec *gids, + const struct idvec *have_uids, + const struct idvec *have_gids, + char *(*getpass_fn) (const char *prompt, + uid_t id, int is_group, + void *pwd_or_grp, void *hook), + void *getpass_hook, + error_t (*verify_fn) (const char *password, + uid_t id, int is_group, + void *pwd_or_grp, void *hook), + void *verify_hook); + +/* Return a string representation of the ids in IDVEC, each id separated by + the string SEP (default ","). SHOW_VALUES and SHOW_NAMES reflect how each + id is printed (if SHOW_NAMES is true values are used where names aren't + available); if both are true, the `VALUE(NAME)' format is used. + ID_NAME_FN is used to map each id to a name; it should return a malloced + string, which will be freed here. The empty string is returned for an + empty list, and 0 for an allocation error. */ +char *idvec_rep (const struct idvec *idvec, + int show_values, int show_names, + char *(*id_name_fn) (uid_t id), + const char *sep); + +/* Like idvec_rep, mapping ids to user names. */ +char *idvec_uids_rep (const struct idvec *idvec, + int show_values, int show_names, + const char *sep); + +/* Like idvec_rep, mapping ids to group names. */ +char *idvec_gids_rep (const struct idvec *idvec, + int show_values, int show_names, + const char *sep); + +#endif /* __IDVEC_H__ */ diff --git a/libshouldbeinlibc/lcm.c b/libshouldbeinlibc/lcm.c new file mode 100644 index 00000000..606f4eba --- /dev/null +++ b/libshouldbeinlibc/lcm.c @@ -0,0 +1,46 @@ +/* Lcm (least common multiple), and gcd (greatest common divisor) + + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* There are probably more efficient ways to do these... */ + +/* Return the greatest common divisor of p & q. */ +inline long +gcd (long p, long q) +{ + if (p == 0) + return q; + else if (q == 0) + return p; + else if (p == q) + return p; + else if (q > p) + return gcd (q, p); + else + return gcd (q, p % q); +} + +/* Return the least common multiple of p & q. */ +long +lcm (long p, long q) +{ + return (p / gcd (p, q)) * q; +} diff --git a/libshouldbeinlibc/localhost.c b/libshouldbeinlibc/localhost.c new file mode 100644 index 00000000..9b7d4e09 --- /dev/null +++ b/libshouldbeinlibc/localhost.c @@ -0,0 +1,75 @@ +/* A slightly more convenient wrapper for gethostname + + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* Return the name of the localhost. This is just a wrapper for gethostname, + which takes care of allocating a big enough buffer, and caches the result + after the first call (so the result should be copied before modification). + If something goes wrong, 0 is returned, and errno set. */ +char * +localhost () +{ + static char *buf = 0; + static size_t buf_len = 0; + + if (! buf) + { + do { + errno = 0; + + if (buf) { + char *new; + buf_len += buf_len; + new = realloc (buf, buf_len); + if (! new) + { + free (buf); + buf = 0; + errno = ENOMEM; + return 0; + } + else + buf = new; + } else { + buf_len = 128; /* Initial guess */ + buf = malloc (buf_len); + if (! buf) + { + errno = ENOMEM; + return 0; + } + } + } while ((gethostname(buf, buf_len) == 0 && !memchr (buf, '\0', buf_len)) + || errno == ENAMETOOLONG); + + if (errno) + /* gethostname failed, abort. */ + { + free (buf); + buf = 0; + } + } + + return buf; +} diff --git a/libshouldbeinlibc/maptime-funcs.c b/libshouldbeinlibc/maptime-funcs.c new file mode 100644 index 00000000..080e3ae6 --- /dev/null +++ b/libshouldbeinlibc/maptime-funcs.c @@ -0,0 +1,5 @@ +#define MAPTIME_DEFINE_EI +#include <errno.h> +#include <sys/types.h> +#include <sys/time.h> +#include "maptime.h" diff --git a/libshouldbeinlibc/maptime.c b/libshouldbeinlibc/maptime.c new file mode 100644 index 00000000..f0b69db4 --- /dev/null +++ b/libshouldbeinlibc/maptime.c @@ -0,0 +1,84 @@ +/* Support for mach's mapped time + + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <fcntl.h> +#include <hurd.h> +#include <device/device.h> + +#include "maptime.h" + +/* Return the mach mapped time page in MTIME. If USE_MACH_DEV is false, then + the hurd time device DEV_NAME, or "/dev/time" if DEV_NAME is 0, is + used. If USE_MACH_DEV is true, the mach device DEV_NAME, or "time" if + DEV_NAME is 0, is used; this is a privileged operation. The mapped time + may be converted to a struct timeval at any time using maptime_read. */ +error_t +maptime_map (int use_mach_dev, char *dev_name, + volatile struct mapped_time_value **mtime) +{ + error_t err; + mach_port_t memobj; + + if (use_mach_dev) + { + device_t device; + mach_port_t device_master; + + err = get_privileged_ports (0, &device_master); + if (err) + return err; + + err = device_open (device_master, 0, dev_name ?: "time", &device); + mach_port_deallocate (mach_task_self (), device_master); + if (err) + return err; + + err = device_map (device, VM_PROT_READ, 0, sizeof *mtime, &memobj, 0); + + /* Deallocate the device port. The mapping is independent of + this port. */ + mach_port_deallocate (mach_task_self (), device); + } + else + { + mach_port_t wr_memobj; + file_t node = file_name_lookup (dev_name ?: "/dev/time", O_RDONLY, 0); + + if (node == MACH_PORT_NULL) + return errno; + + err = io_map (node, &memobj, &wr_memobj); + if (!err && wr_memobj != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), wr_memobj); + + mach_port_deallocate (mach_task_self (), node); + } + + if (! err) + { + *mtime = 0; + err = + vm_map (mach_task_self (), (vm_address_t *)mtime, sizeof *mtime, 0, 1, + memobj, 0, 0, VM_PROT_READ, VM_PROT_READ, VM_INHERIT_NONE); + mach_port_deallocate (mach_task_self (), memobj); + } + + return err; +} diff --git a/libshouldbeinlibc/maptime.h b/libshouldbeinlibc/maptime.h new file mode 100644 index 00000000..947ad640 --- /dev/null +++ b/libshouldbeinlibc/maptime.h @@ -0,0 +1,61 @@ +/* Support for mach's mapped time + + Copyright (C) 1996, 1997, 2000, 2007 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef __MAPTIME_H__ +#define __MAPTIME_H__ + +#include <mach/time_value.h> +#include <sys/time.h> +#include <errno.h> +#include <features.h> + +#ifdef MAPTIME_DEFINE_EI +#define MAPTIME_EI +#else +#define MAPTIME_EI __extern_inline +#endif + +/* Return the mach mapped time page in MTIME. If USE_MACH_DEV is false, then + the hurd time device DEV_NAME, or "/dev/time" if DEV_NAME is 0, is + used. If USE_MACH_DEV is true, the mach device DEV_NAME, or "time" if + DEV_NAME is 0, is used; this is a privileged operation. The mapped time + may be converted to a struct timeval at any time using maptime_read. */ +error_t maptime_map (int use_mach_dev, char *dev_name, + volatile struct mapped_time_value **mtime); + +extern void maptime_read (volatile struct mapped_time_value *mtime, struct timeval *tv); + +#if defined(__USE_EXTERN_INLINES) || defined(MAPTIME_DEFINE_EI) + +/* Read the current time from MTIME into TV. This should be very fast. */ +MAPTIME_EI void +maptime_read (volatile struct mapped_time_value *mtime, struct timeval *tv) +{ + do + { + tv->tv_sec = mtime->seconds; + tv->tv_usec = mtime->microseconds; + } + while (tv->tv_sec != mtime->check_seconds); +} + +#endif /* Use extern inlines. */ + +#endif /* __MAPTIME_H__ */ diff --git a/libshouldbeinlibc/nullauth.c b/libshouldbeinlibc/nullauth.c new file mode 100644 index 00000000..3a98e558 --- /dev/null +++ b/libshouldbeinlibc/nullauth.c @@ -0,0 +1,45 @@ +/* Drop all authentication credentials. + + Copyright (C) 2013 Free Software Foundation, Inc. + + Written by Justus Winter <4winter@informatik.uni-hamburg.de> + + This file is part of the GNU Hurd. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <hurd.h> + +/* Obtain an empty authentication handle and use it for further + authentication purposes. This effectively drops all Unix + privileges. */ +error_t +setnullauth (void) +{ + error_t err; + + auth_t nullauth; + err = auth_makeauth (getauth (), + NULL, MACH_MSG_TYPE_COPY_SEND, 0, + NULL, 0, + NULL, 0, + NULL, 0, + NULL, 0, + &nullauth); + if (err) + return err; + + err = setauth (nullauth); + return err; +} diff --git a/libshouldbeinlibc/nullauth.h b/libshouldbeinlibc/nullauth.h new file mode 100644 index 00000000..efdb5f3a --- /dev/null +++ b/libshouldbeinlibc/nullauth.h @@ -0,0 +1,31 @@ +/* Drop all authentication credentials. + + Copyright (C) 2013 Free Software Foundation, Inc. + + Written by Justus Winter <4winter@informatik.uni-hamburg.de> + + This file is part of the GNU Hurd. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef __NULLAUTH_H__ +#define __NULLAUTH_H__ + +/* Obtain an empty authentication handle and use it for further + authentication purposes. This effectively drops all Unix + privileges. */ +error_t +setnullauth (void); + +#endif /* __NULLAUTH_H__ */ diff --git a/libshouldbeinlibc/portinfo.c b/libshouldbeinlibc/portinfo.c new file mode 100644 index 00000000..e6305c6e --- /dev/null +++ b/libshouldbeinlibc/portinfo.c @@ -0,0 +1,158 @@ +/* Print information about a task's ports + + Copyright (C) 1996,98,99,2002 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <sys/types.h> +#include <sys/mman.h> + +#include "portinfo.h" + +/* Prints info about NAME in TASK to STREAM, in a way described by the flags + in SHOW. If TYPE is non-zero, it should be what mach_port_type returns + for NAME. */ +error_t +print_port_info (mach_port_t name, mach_port_type_t type, task_t task, + unsigned show, FILE *stream) +{ + int hex_names = (show & PORTINFO_HEX_NAMES); + int first = 1; + void comma () + { + if (first) + first = 0; + else + fprintf (stream, ", "); + } + void prefs (mach_port_right_t right) + { + mach_port_urefs_t refs; + error_t err = mach_port_get_refs (task, name, right, &refs); + if (! err) + fprintf (stream, " (refs: %zu)", refs); + } + + if (type == 0) + { + error_t err = mach_port_type (task, name, &type); + if (err) + return err; + } + + fprintf (stream, hex_names ? "%#6zx: " : "%6zd: ", name); + + if (type & MACH_PORT_TYPE_RECEIVE) + { + comma (); + fprintf (stream, "receive"); + if (show & PORTINFO_DETAILS) + { + struct mach_port_status status; + error_t err = mach_port_get_receive_status (task, name, &status); + if (! err) + { + fprintf (stream, " ("); + if (status.mps_pset != MACH_PORT_NULL) + fprintf (stream, + hex_names ? "port-set: %#zx, " : "port-set: %zd, ", + status.mps_pset); + fprintf (stream, "seqno: %zu", status.mps_seqno); + if (status.mps_mscount) + fprintf (stream, ", ms-count: %zu", status.mps_mscount); + if (status.mps_qlimit != MACH_PORT_QLIMIT_DEFAULT) + fprintf (stream, ", qlimit: %zu", status.mps_qlimit); + if (status.mps_msgcount) + fprintf (stream, ", msgs: %zu", status.mps_msgcount); + fprintf (stream, "%s%s%s)", + status.mps_srights ? ", send-rights" : "", + status.mps_pdrequest ? ", pd-req" : "", + status.mps_nsrequest ? ", ns-req" : ""); + } + } + } + if (type & MACH_PORT_TYPE_SEND) + { + comma (); + fprintf (stream, "send"); + if (show & PORTINFO_DETAILS) + prefs (MACH_PORT_RIGHT_SEND); + } + if (type & MACH_PORT_TYPE_SEND_ONCE) + { + comma (); + fprintf (stream, "send-once"); + } + if (type & MACH_PORT_TYPE_DEAD_NAME) + { + comma (); + fprintf (stream, "dead-name"); + if (show & PORTINFO_DETAILS) + prefs (MACH_PORT_RIGHT_DEAD_NAME); + } + if (type & MACH_PORT_TYPE_PORT_SET) + { + comma (); + fprintf (stream, "port-set"); + if (show & PORTINFO_DETAILS) + { + mach_port_t *members = 0; + mach_msg_type_number_t members_len = 0, i; + error_t err = + mach_port_get_set_status (task, name, &members, &members_len); + if (! err) + { + if (members_len == 0) + fprintf (stream, " (empty)"); + else + { + fprintf (stream, hex_names ? " (%#zx" : " (%zu", members[0]); + for (i = 1; i < members_len; i++) + fprintf (stream, hex_names ? ", %#zx" : ", %zu", + members[i]); + fprintf (stream, ")"); + munmap ((caddr_t) members, members_len * sizeof *members); + } + } + } + } + putc ('\n', stream); + + return 0; +} + +/* Prints info about every port in TASK that has a type in ONLY to STREAM. */ +error_t +print_task_ports_info (task_t task, mach_port_type_t only, + unsigned show, FILE *stream) +{ + mach_port_t *names = 0; + mach_port_type_t *types = 0; + mach_msg_type_number_t names_len = 0, types_len = 0, i; + error_t err = mach_port_names (task, &names, &names_len, &types, &types_len); + + if (err) + return err; + + for (i = 0; i < names_len; i++) + if (types[i] & only) + print_port_info (names[i], types[i], task, show, stream); + + munmap ((caddr_t) names, names_len * sizeof *names); + munmap ((caddr_t) types, types_len * sizeof *types); + + return 0; +} diff --git a/libshouldbeinlibc/portinfo.h b/libshouldbeinlibc/portinfo.h new file mode 100644 index 00000000..143c2898 --- /dev/null +++ b/libshouldbeinlibc/portinfo.h @@ -0,0 +1,58 @@ +/* Print information about a task's ports + + Copyright (C) 1996, 1999 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef __PORTINFO_H__ +#define __PORTINFO_H__ + +#include <stdio.h> +#include <errno.h> +#include <mach.h> + +#include <portxlate.h> + +/* Flags describing what to show. */ +#define PORTINFO_DETAILS 0x1 +#define PORTINFO_MEMBERS 0x4 +#define PORTINFO_HEX_NAMES 0x8 + +/* Prints info about NAME in TASK to STREAM, in a way described by the flags + in SHOW. If TYPE is non-zero, it should be what mach_port_type returns + for NAME. */ +error_t print_port_info (mach_port_t name, mach_port_type_t type, task_t task, + unsigned show, FILE *stream); + +/* Prints info about every port in TASK that has a type in ONLY to STREAM. */ +error_t print_task_ports_info (task_t task, mach_port_type_t only, + unsigned show, FILE *stream); + +/* Prints info about NAME translated through X to STREAM, in a way described + by the flags in SHOW. If TYPE is non-zero, it should be what + mach_port_type returns for NAME in X->to_task. */ +error_t print_xlated_port_info (mach_port_t name, mach_port_type_t type, + struct port_name_xlator *x, + unsigned show, FILE *stream); + +/* Prints info about every port common to both tasks in X, but only if the + port in X->from_task has a type in ONLY, to STREAM. */ +error_t print_xlated_task_ports_info (struct port_name_xlator *x, + mach_port_type_t only, + unsigned show, FILE *stream); + +#endif /* __PORTINFO_H__ */ diff --git a/libshouldbeinlibc/portxlate.c b/libshouldbeinlibc/portxlate.c new file mode 100644 index 00000000..f78abbf1 --- /dev/null +++ b/libshouldbeinlibc/portxlate.c @@ -0,0 +1,179 @@ +/* Translate mach port names between two tasks + + Copyright (C) 1996,99,2002 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/mman.h> + +#include "portxlate.h" + +/* Return a new port name translator translating names between FROM_TASK and + TO_TASK, in XLATOR, or an error. */ +error_t +port_name_xlator_create (mach_port_t from_task, mach_port_t to_task, + struct port_name_xlator **xlator) +{ + error_t err; + struct port_name_xlator *x = malloc (sizeof (struct port_name_xlator)); + + if (! x) + return ENOMEM; + + mach_port_mod_refs (mach_task_self (), from_task, MACH_PORT_RIGHT_SEND, +1); + x->from_task = from_task; + mach_port_mod_refs (mach_task_self (), to_task, MACH_PORT_RIGHT_SEND, +1); + x->to_task = to_task; + x->to_names = 0; + x->to_types = 0; + x->to_names_len = 0; + x->to_types_len = 0; + + /* Cache a list of names in TO_TASK. */ + err = mach_port_names (to_task, + &x->to_names, &x->to_names_len, + &x->to_types, &x->to_types_len); + + if (! err) + /* Make an array to hold ports from TO_TASK which have been translated + into our namespace. */ + { + x->ports = malloc (sizeof (mach_port_t) * x->to_names_len); + if (x->ports) + { + unsigned int i; + for (i = 0; i < x->to_names_len; i++) + x->ports[i] = MACH_PORT_NULL; + } + else + { + munmap ((caddr_t) x->to_names, + x->to_names_len * sizeof (mach_port_t)); + munmap ((caddr_t) x->to_types, + x->to_types_len * sizeof (mach_port_type_t)); + + mach_port_deallocate (mach_task_self (), x->to_task); + mach_port_deallocate (mach_task_self (), x->from_task); + + err = ENOMEM; + } + } + + if (err) + free (x); + else + *xlator = x; + + return err; +} + +/* Free the port name translator X and any resources it holds. */ +void +port_name_xlator_free (struct port_name_xlator *x) +{ + unsigned int i; + + for (i = 0; i < x->to_names_len; i++) + if (x->ports[i] != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), x->ports[i]); + free (x->ports); + + munmap ((caddr_t) x->to_names, x->to_names_len * sizeof (mach_port_t)); + munmap ((caddr_t) x->to_types, x->to_types_len * sizeof (mach_port_type_t)); + + mach_port_deallocate (mach_task_self (), x->to_task); + mach_port_deallocate (mach_task_self (), x->from_task); + + free (x); +} + +/* Translate the port FROM between the tasks in X, returning the translated + name in TO, and the types of TO in TO_TYPE, or an error. If TYPE is + non-zero, it should be what mach_port_type returns for FROM. */ +error_t +port_name_xlator_xlate (struct port_name_xlator *x, + mach_port_t from, mach_port_type_t from_type, + mach_port_t *to, mach_port_type_t *to_type) +{ + error_t err; + mach_port_t port; + mach_msg_type_number_t i; + mach_msg_type_name_t aquired_type; + mach_msg_type_name_t valid_to_types; + + if (from_type == 0) + { + error_t err = mach_port_type (x->from_task, from, &from_type); + if (err) + return err; + } + + if (from_type & MACH_PORT_TYPE_RECEIVE) + valid_to_types = MACH_PORT_TYPE_SEND; + else if (from_type & MACH_PORT_TYPE_SEND) + valid_to_types = MACH_PORT_TYPE_SEND | MACH_PORT_TYPE_RECEIVE; + else + return EKERN_INVALID_RIGHT; + + /* Translate the name FROM, in FROM_TASK's namespace into our namespace. */ + err = + mach_port_extract_right (x->from_task, from, + ((from_type & MACH_PORT_TYPE_RECEIVE) + ? MACH_MSG_TYPE_MAKE_SEND + : MACH_MSG_TYPE_COPY_SEND), + &port, + &aquired_type); + + if (err) + return err; + + /* Look for likely candidates in TO_TASK's namespace to test against PORT. */ + for (i = 0; i < x->to_names_len; i++) + { + if (x->ports[i] == MACH_PORT_NULL && (x->to_types[i] & valid_to_types)) + /* Port I shows possibilities... */ + { + err = + mach_port_extract_right (x->to_task, + x->to_names[i], + ((x->to_types[i] & MACH_PORT_TYPE_RECEIVE) + ? MACH_MSG_TYPE_MAKE_SEND + : MACH_MSG_TYPE_COPY_SEND), + &x->ports[i], + &aquired_type); + if (err) + x->to_types[i] = 0; /* Don't try to fetch this port again. */ + } + + if (x->ports[i] == port) + /* We win! Port I in TO_TASK is the same as PORT. */ + break; + } + + mach_port_deallocate (mach_task_self (), port); + + if (i < x->to_names_len) + /* Port I is the right translation; return its name in TO_TASK. */ + { + *to = x->to_names[i]; + *to_type = x->to_types[i]; + return 0; + } + else + return EKERN_INVALID_NAME; +} diff --git a/libshouldbeinlibc/portxlate.h b/libshouldbeinlibc/portxlate.h new file mode 100644 index 00000000..13ddb148 --- /dev/null +++ b/libshouldbeinlibc/portxlate.h @@ -0,0 +1,67 @@ +/* Translate mach port names between two tasks + + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef __PORTXLATE_H__ +#define __PORTXLATE_H__ + +#include <errno.h> +#include <mach.h> + +/* A data structure specifying two tasks, and info used to translate port + names between them. */ +struct port_name_xlator +{ + /* The tasks between which we are translating port names. */ + mach_port_t from_task; + mach_port_t to_task; + + /* True if we're translating receive rights in FROM_TASK; otherwise, we're + translating send rights. */ + int from_is_receive; + + /* Arrays of port names and type masks from TO_TASK, fetched by + mach_port_names. These are vm_allocated. */ + mach_port_t *to_names; + mach_msg_type_number_t to_names_len; + mach_port_type_t *to_types; + mach_msg_type_number_t to_types_len; + + /* An array of rights in the current task to the ports in TO_NAMES/TO_TASK, + or MACH_PORT_NULL, indicating that none has been fetched yet. + This vector is malloced. */ + mach_port_t *ports; +}; + +/* Return a new port name translator translating names between FROM_TASK and + TO_TASK, in XLATOR, or an error. */ +error_t port_name_xlator_create (mach_port_t from_task, mach_port_t to_task, + struct port_name_xlator **xlator); + +/* Free the port name translator X and any resources it holds. */ +void port_name_xlator_free (struct port_name_xlator *x); + +/* Translate the port FROM between the tasks in X, returning the translated + name in TO, and the types of TO in TO_TYPE, or an error. If TYPE is + non-zero, it should be what mach_port_type returns for FROM. */ +error_t port_name_xlator_xlate (struct port_name_xlator *x, + mach_port_t from, mach_port_type_t from_type, + mach_port_t *to, mach_port_type_t *to_type); + +#endif /* __PORTXLATE_H__ */ diff --git a/libshouldbeinlibc/shared-dom.c b/libshouldbeinlibc/shared-dom.c new file mode 100644 index 00000000..0115fced --- /dev/null +++ b/libshouldbeinlibc/shared-dom.c @@ -0,0 +1,54 @@ +/* Deduce the shared portion of two hostnames + + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <string.h> + +/* Returns a pointer into HOST1 that is the part of the domain shared with + HOST2. If the two do not share anything, the return value will point to + the end of HOST1. If either host is NULL, NULL is returned. */ +char * +shared_domain (char *host1, char *host2) +{ + char *shared, *e1, *e2; + + if (!host1 || !host2) + return 0; + + /* Now compare HOST1 and HOST2 from the end. */ + e2 = host2 + strlen (host2); + e1 = host1 + strlen (host1); + shared = e1; + + /* Ignore `absolute' syntax. */ + if (*e1 == '.') + e1--; + if (*e2 == '.') + e2--; + + while (e1 > host1 && e2 > host2 && *e2 == *e1) + { + if (*e1 == '.') + shared = e1; /* A common domain level has been passed. */ + e1--; + e2--; + } + + return shared; +} diff --git a/libshouldbeinlibc/termsize.c b/libshouldbeinlibc/termsize.c new file mode 100644 index 00000000..46666975 --- /dev/null +++ b/libshouldbeinlibc/termsize.c @@ -0,0 +1,54 @@ +/* Function to try and deduce what size the terminal is + + Copyright (C) 1995 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <sys/ioctl.h> + +/* Returns what we think is the size of the terminal attached to + file descriptor FD, of type TYPE, in WIDTH and/or HEIGHT. If FD is + negative, the terminal isn't queried, and if TYPE is NULL, it isn't used. + Both WIDTH and HEIGHT may be NULL if only partial information is needed. + True is returned upon success. Even if false is returned, both output + values are still written, with 0 for unknown, in case partial information + is useful. */ +int +deduce_term_size (int fd, char *type, int *width, int *height) +{ + int w = 0, h = 0; + struct winsize ws; + + if (fd >= 0 && ioctl (fd, TIOCGWINSZ, &ws) == 0) + /* Look at the actual terminal. */ + { + w = ws.ws_col; + h = ws.ws_row; + } + if (((width && !w) || (height && !h)) && type) + /* Try the terminal type. */ + { + /* XXX */ + } + + if (width) + *width = w; + if (height) + *height = h; + + return (!width || w) && (!height && h); +} diff --git a/libshouldbeinlibc/timefmt.c b/libshouldbeinlibc/timefmt.c new file mode 100644 index 00000000..a28f58bd --- /dev/null +++ b/libshouldbeinlibc/timefmt.c @@ -0,0 +1,359 @@ +/* Routines for formatting time + + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <string.h> +#include <sys/time.h> +#include <time.h> + +#include "timefmt.h" + +#define SECOND 1 +#define MINUTE 60 +#define HOUR (60*MINUTE) +#define DAY (24*HOUR) +#define WEEK (7*DAY) +#define MONTH (31*DAY) /* Not strictly accurate, but oh well. */ +#define YEAR (365*DAY) /* ditto */ + +/* Returns the number of digits in the integer N. */ +static unsigned +int_len (unsigned n) +{ + unsigned len = 1; + while (n >= 10) + { + n /= 10; + len++; + } + return len; +} + +/* Returns TV1 divided by TV2. */ +static unsigned +tv_div (struct timeval *tv1, struct timeval *tv2) +{ + return + tv2->tv_sec + ? tv1->tv_sec / tv2->tv_sec + : (tv1->tv_usec / tv2->tv_usec + + (tv1->tv_sec ? tv1->tv_sec * 1000000 / tv2->tv_usec : 0)); +} + +/* Returns true if TV is zero. */ +static inline int +tv_is_zero (struct timeval *tv) +{ + return tv->tv_sec == 0 && tv->tv_usec == 0; +} + +/* Returns true if TV1 >= TV2. */ +static inline int +tv_is_ge (struct timeval *tv1, struct timeval *tv2) +{ + return + tv1->tv_sec > tv2->tv_sec + || (tv1->tv_sec == tv2->tv_sec && tv1->tv_usec >= tv2->tv_usec); +} + +/* Format into BUF & BUF_LEN the time interval represented by TV, trying to + make the result less than WIDTH characters wide. The number of characters + used is returned. */ +size_t +fmt_named_interval (struct timeval *tv, size_t width, + char *buf, size_t buf_len) +{ + struct tscale + { + struct timeval thresh; /* Minimum time to use this scale. */ + struct timeval unit; /* Unit this scale is based on. */ + struct timeval frac_thresh; /* If a emitting a single digit of precision + will cause at least this much error, also + emit a single fraction digit. */ + char *sfxs[5]; /* Names to use, in descending length. */ + } + time_scales[] = + { + {{2*YEAR, 0}, {YEAR, 0}, {MONTH, 0},{" years", "years", "yrs", "y", 0 }}, + {{3*MONTH, 0}, {MONTH, 0}, {WEEK, 0}, {" months","months","mo", 0 }}, + {{2*WEEK, 0}, {WEEK, 0}, {DAY, 0}, {" weeks", "weeks", "wks", "w", 0 }}, + {{2*DAY, 0}, {DAY, 0}, {HOUR, 0}, {" days", "days", "dys", "d", 0 }}, + {{2*HOUR, 0}, {HOUR, 0}, {MINUTE, 0},{" hours","hours", "hrs", "h", 0 }}, + {{2*MINUTE, 0},{MINUTE, 0},{1, 0}, {" minutes","min", "mi", "m", 0 }}, + {{1, 100000}, {1, 0}, {0, 100000},{" seconds", "sec", "s", 0 }}, + {{1, 0}, {1, 0}, {0, 0}, {" second", "sec", "s", 0 }}, + {{0, 1100}, {0, 1000}, {0, 100}, {" milliseconds", "ms", 0 }}, + {{0, 1000}, {0, 1000}, {0, 0}, {" millisecond", "ms", 0 }}, + {{0, 2}, {0, 1}, {0, 0}, {" microseconds", "us", 0 }}, + {{0, 1}, {0, 1}, {0, 0}, {" microsecond", "us", 0 }}, + {{0, 0} } + }; + struct tscale *ts = time_scales; + + if (width <= 0 || width >= buf_len) + width = buf_len - 1; + + for (ts = time_scales; !tv_is_zero (&ts->thresh); ts++) + if (tv_is_ge (tv, &ts->thresh)) + { + char **sfx; + struct timeval *u = &ts->unit; + unsigned num = tv_div (tv, u); + unsigned frac = 0; + unsigned num_len = int_len (num); + + if (num < 10 + && !tv_is_zero (&ts->frac_thresh) + && tv_is_ge (tv, &ts->frac_thresh)) + /* Calculate another place of prec, but only for low numbers. */ + { + /* TV times 10. */ + struct timeval tv10 = + { tv->tv_sec * 10 + tv->tv_usec / 100000, + (tv->tv_usec % 100000) * 10 }; + frac = tv_div (&tv10, u) - num * 10; + if (frac) + num_len += 2; /* Account for the extra `.' + DIGIT. */ + } + + /* While we have a choice, find a suffix that fits in WIDTH. */ + for (sfx = ts->sfxs; sfx[1]; sfx++) + if (num_len + strlen (*sfx) <= width) + break; + + if (!sfx[1] && frac) + /* We couldn't find a suffix that fits, and we're printing a + fraction digit. Sacrifice the fraction to make it fit. */ + { + num_len -= 2; + frac = 0; + for (sfx = ts->sfxs; sfx[1]; sfx++) + if (num_len + strlen (*sfx) <= width) + break; + } + + if (!sfx[1]) + /* Still couldn't find a suffix that fits. Oh well, use the best. */ + sfx--; + + if (frac) + return snprintf (buf, buf_len, "%d.%d%s", num, frac, *sfx); + else + return snprintf (buf, buf_len, "%d%s", num, *sfx); + } + + return sprintf (buf, "0"); /* Whatever */ +} + +/* Prints the number of units of size UNIT in *SECS, subtracting them from + *SECS, to BUF (the result *must* fit!), followed by SUFFIX; if the number + of units is zero, however, and *LEADING_ZEROS is false, print nothing (and + if something *is* printed, set *LEADING_ZEROS to true). MIN_WIDTH is the + minimum *total width* (including other fields) needed to print these + units. WIDTH is the amount of (total) space available. The number of + characters printed is returned. */ +static size_t +add_field (int *secs, int unit, int *leading_zeros, + size_t min_width, char *suffix, + size_t width, char *buf) +{ + int units = *secs / unit; + if (units || (width >= min_width && *leading_zeros)) + { + *secs -= units * unit; + *leading_zeros = 1; + return + sprintf (buf, + (width == min_width ? "%d%s" + : width == min_width + 1 ? "%2d%s" + : "%02d%s"), + units, suffix); + } + else + return 0; +} + +/* Format into BUF & BUF_LEN the time interval represented by TV, using + HH:MM:SS notation where possible, with FRAC_PLACES digits after the + decimal point, and trying to make the result less than WIDTH characters + wide. If LEADING_ZEROS is true, then any fields that are zero-valued, but + would fit in the given width are printed. If FRAC_PLACES is negative, + then any space remaining after printing the time, up to WIDTH, is used for + the fraction. The number of characters used is returned. */ +size_t +fmt_seconds (struct timeval *tv, int leading_zeros, int frac_places, + size_t width, char *buf, size_t buf_len) +{ + char *p = buf; + int secs = tv->tv_sec; + + if (width <= 0 || width >= buf_len) + width = buf_len - 1; + + if (tv->tv_sec > DAY) + return fmt_named_interval (tv, width, buf, buf_len); + + if (frac_places > 0) + width -= frac_places + 1; + + /* See if this time won't fit at all in fixed format. */ + if ((secs > 10*HOUR && width < 8) + || (secs > HOUR && width < 7) + || (secs > 10*MINUTE && width < 5) + || (secs > MINUTE && width < 4) + || (secs > 10 && width < 2) + || width < 1) + return fmt_named_interval (tv, width, buf, buf_len); + + p += add_field (&secs, HOUR, &leading_zeros, 7, ":", width, p); + p += add_field (&secs, MINUTE, &leading_zeros, 4, ":", width, p); + p += add_field (&secs, SECOND, &leading_zeros, 1, "", width, p); + + if (frac_places < 0 && (p - buf) < (int) width - 2) + /* If FRAC_PLACES is < 0, then use any space remaining before WIDTH. */ + frac_places = width - (p - buf) - 1; + + if (frac_places > 0) + /* Print fractions of a second. */ + { + int frac = tv->tv_usec, i; + for (i = 6; i > frac_places; i--) + frac /= 10; + return (p - buf) + sprintf (p, ".%0*d", frac_places, frac); + } + else + return (p - buf); +} + +/* Format into BUF & BUF_LEN the time interval represented by TV, using HH:MM + notation where possible, and trying to make the result less than WIDTH + characters wide. If LEADING_ZEROS is true, then any fields that are + zero-valued, but would fit in the given width are printed. The number of + characters used is returned. */ +size_t +fmt_minutes (struct timeval *tv, int leading_zeros, + size_t width, char *buf, size_t buf_len) +{ + char *p = buf; + int secs = tv->tv_sec; + + if (width <= 0 || width >= buf_len) + width = buf_len - 1; + + if (secs > DAY) + return fmt_named_interval (tv, width, buf, buf_len); + + /* See if this time won't fit at all in fixed format. */ + if ((secs > 10*HOUR && width < 5) + || (secs > HOUR && width < 4) + || (secs > 10*MINUTE && width < 2) + || width < 1) + return fmt_named_interval (tv, width, buf, buf_len); + + p += add_field (&secs, HOUR, &leading_zeros, 4, ":", width, p); + p += add_field (&secs, MINUTE, &leading_zeros, 1, "", width, p); + + return p - buf; +} + +/* Format into BUF & BUF_LEN the absolute time represented by TV. An attempt + is made to fit the result in less than WIDTH characters, by omitting + fields implied by the current time, NOW (if NOW is 0, then no assumption + is made, so the resulting times will be somewhat long). The number of + characters used is returned. */ +size_t +fmt_past_time (struct timeval *tv, struct timeval *now, + size_t width, char *buf, size_t buf_len) +{ + static char *time_fmts[] = { "%-r", "%-l:%M%p", "%-l%p", 0 }; + static char *week_fmts[] = { "%A", "%a", 0 }; + static char *month_fmts[] = { "%A %-d", "%a %-d", "%a%-d", 0 }; + static char *date_fmts[] = + { "%A, %-d %B", "%a, %-d %b", "%-d %B", "%-d %b", "%-d%b", 0 }; + static char *year_fmts[] = + { "%A, %-d %B %Y", "%a, %-d %b %Y", "%a, %-d %b %y", "%-d %b %y", "%-d%b%y", 0 }; + struct tm tm; + int used = 0; /* Number of characters generated. */ + long diff = now ? (now->tv_sec - tv->tv_sec) : tv->tv_sec; + + if (diff < 0) + diff = -diff; /* XXX */ + + bcopy (localtime ((time_t *) &tv->tv_sec), &tm, sizeof tm); + + if (width <= 0 || width >= buf_len) + width = buf_len - 1; + + if (diff < DAY) + { + char **fmt; + for (fmt = time_fmts; *fmt && !used; fmt++) + used = strftime (buf, width + 1, *fmt, &tm); + if (! used) + /* Nothing worked, perhaps WIDTH is too small, but BUF_LEN will work. + We know FMT is one past the end the array, so FMT[-1] should be + the shortest possible option. */ + used = strftime (buf, buf_len, fmt[-1], &tm); + } + else + { + static char *seps[] = { ", ", " ", "", 0 }; + char **fmt, **dfmt, **dfmts, **sep; + + if (diff < WEEK) + dfmts = week_fmts; + else if (diff < MONTH) + dfmts = month_fmts; + else if (diff < YEAR) + dfmts = date_fmts; + else + dfmts = year_fmts; + + /* This ordering (date varying most quickly, then the separator, then + the time) preserves time detail as long as possible, and seems to + produce a graceful degradation of the result with decreasing widths. */ + for (fmt = time_fmts; *fmt && !used; fmt++) + for (sep = seps; *sep && !used; sep++) + for (dfmt = dfmts; *dfmt && !used; dfmt++) + { + char whole_fmt[strlen (*dfmt) + strlen (*sep) + strlen (*fmt) + 1]; + char *end = whole_fmt; + + end = stpcpy (end, *dfmt); + end = stpcpy (end, *sep); + stpcpy (end, *fmt); + + used = strftime (buf, width + 1, whole_fmt, &tm); + } + + if (! used) + /* No concatenated formats worked, try just date formats. */ + for (dfmt = dfmts; *dfmt && !used; dfmt++) + used = strftime (buf, width + 1, *dfmt, &tm); + + if (! used) + /* Absolutely nothing has worked, perhaps WIDTH is too small, but + BUF_LEN will work. We know DFMT is one past the end the array, so + DFMT[-1] should be the shortest possible option. */ + used = strftime (buf, buf_len, dfmt[-1], &tm); + } + + return used; +} diff --git a/libshouldbeinlibc/timefmt.h b/libshouldbeinlibc/timefmt.h new file mode 100644 index 00000000..134cf56a --- /dev/null +++ b/libshouldbeinlibc/timefmt.h @@ -0,0 +1,58 @@ +/* Routines for formatting time + + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef __TIMEFMT_H__ +#define __TIMEFMT_H__ + +struct timeval; + +/* Format into BUF & BUF_LEN the time interval represented by TV, trying to + make the result less than WIDTH characters wide. The number of characters + used is returned. */ +size_t fmt_named_interval (struct timeval *tv, size_t width, + char *buf, size_t buf_len); + +/* Format into BUF & BUF_LEN the time interval represented by TV, using + HH:MM:SS notation where possible, with FRAC_PLACES digits after the + decimal point, and trying to make the result less than WIDTH characters + wide. If LEADING_ZEROS is true, then any fields that are zero-valued, but + would fit in the given width are printed. If FRAC_PLACES is negative, + then any space remaining after printing the time, up to WIDTH, is used for + the fraction. The number of characters used is returned. */ +size_t fmt_seconds (struct timeval *tv, int leading_zeros, int frac_places, + size_t width, char *buf, size_t buf_len); + +/* Format into BUF & BUF_LEN the time interval represented by TV, using HH:MM + notation where possible, and trying to make the result less than WIDTH + characters wide. If LEADING_ZEROS is true, then any fields that are + zero-valued, but would fit in the given width are printed. The number of + characters used is returned. */ +size_t fmt_minutes (struct timeval *tv, int leading_zeros, + size_t width, char *buf, size_t buf_len); + +/* Format into BUF & BUF_LEN the absolute time represented by TV. An attempt + is made to fit the result in less than WIDTH characters, by omitting + fields implied by the current time, NOW (if NOW is 0, then no assumptions + are made, so the resulting times will be somewhat long). The number of + characters used is returned. */ +size_t fmt_past_time (struct timeval *tv, struct timeval *now, + size_t width, char *buf, size_t buf_len); + +#endif /* __TIMEFMT_H__ */ diff --git a/libshouldbeinlibc/ugids-argp.c b/libshouldbeinlibc/ugids-argp.c new file mode 100644 index 00000000..43a54d70 --- /dev/null +++ b/libshouldbeinlibc/ugids-argp.c @@ -0,0 +1,174 @@ +/* Parse user and group ids + + Copyright (C) 1997, 1999, 2008 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <argp.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> + +#include "ugids.h" + +#define OA OPTION_ARG_OPTIONAL + +static const struct argp_option options[] = +{ + {"user", 'u', "USER", 0, "Add USER to the effective uids"}, + {"avail-user",'U', "USER", 0, "Add USER to the available uids"}, + {"group", 'g', "GROUP", 0, "Add GROUP to the effective groups"}, + {"avail-group",'G',"GROUP", 0, "Add GROUP to the available groups"}, + { 0 } +}; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + char id_lookup_buf[1024]; + struct ugids_argp_params *params = state->input; + struct ugids *ugids = params->ugids; + + switch (key) + { + uid_t uid; + + case 'u': + case 'U': + case ARGP_KEY_ARG: + case ARGP_KEY_END: + if (key == ARGP_KEY_ARG && !params->parse_user_args) + /* Let someone else parse this argument. */ + return ARGP_ERR_UNKNOWN; + + if (key == ARGP_KEY_END) + { + if (ugids_is_empty (ugids)) + { + if (params->default_user >= 0) + uid = params->default_user; + else if (params->require_ids) + { + argp_error (state, "No ids specified"); + return EINVAL; + } + else + break; + } + else + break; + } + else if (isdigit (*arg)) + uid = atoi (arg); + else if (strcmp (arg, "-") == 0) + break; + else + { + struct passwd _pw, *pw; + int err; + err = getpwnam_r (arg, &_pw, id_lookup_buf, + sizeof id_lookup_buf, &pw); + if (err == 0) + { + if (pw == NULL) + { + argp_failure (state, 10, 0, "%s: Unknown user", arg); + return EINVAL; + } + + uid = pw->pw_uid; + } + else + { + argp_failure (state, 12, err, + "Could not get uid for user: %s", arg); + return err; + } + } + + if (key == ARGP_KEY_ARG || key == ARGP_KEY_END) + { + /* A user arg, which means add the user, and any appropriate + groups. */ + if (!params->user_args_are_effective + && !params->user_args_are_available) + return ugids_set_posix_user (ugids, uid); + else + { + error_t err = 0; + if (params->user_args_are_effective) + err = ugids_add_user (ugids, uid, 0); + if (!err && params->user_args_are_available) + err = ugids_add_user (ugids, uid, 1); + return err; + } + } + else + /* Add an individual specific effective/auxiliary uid. */ + return ugids_add_uid (ugids, uid, key == 'U'); + + case 'g': + case 'G': + if (isdigit (*arg)) + return ugids_add_gid (ugids, atoi (arg), key == 'G'); + else + { + struct group _gr, *gr; + int err = getgrnam_r (arg, &_gr, id_lookup_buf, + sizeof id_lookup_buf, &gr); + if (err == 0) + { + if (gr == NULL) + { + argp_failure (state, 11, 0, "%s: Unknown group", arg); + return EINVAL; + } + + return ugids_add_gid (ugids, gr->gr_gid, key == 'G'); + } + else + { + argp_failure (state, 13, err, + "Could not get gid for group: %s", arg); + return err; + } + } + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +/* Filtering of help output strings for UGIDS_ARGP. */ +static char * +help_filter (int key, const char *text, void *input) +{ + struct ugids_argp_params *params = input; + + /* Describe the optional behavior of parsing normal args as ugids. */ + if (key == ARGP_KEY_HELP_ARGS_DOC && params->parse_user_args) + return strdup ("[USER...]"); + + return (char *)text; +} + +/* A parser for selecting a set of ugids. */ +struct argp ugids_argp = { options, parse_opt, 0, 0, 0, help_filter }; diff --git a/libshouldbeinlibc/ugids-auth.c b/libshouldbeinlibc/ugids-auth.c new file mode 100644 index 00000000..0e4f84dc --- /dev/null +++ b/libshouldbeinlibc/ugids-auth.c @@ -0,0 +1,53 @@ +/* Translate user and group ids to/from auth ports + + Copyright (C) 1997 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <hurd.h> + +#include "idvec.h" +#include "ugids.h" + +/* Make an auth port from UGIDS and return it in AUTH, using authority in + both the auth port FROM and the current auth port. */ +error_t +ugids_make_auth (const struct ugids *ugids, + const auth_t *from, size_t num_from, + auth_t *auth) +{ + auth_t cur_auth = getauth (); + error_t err = + auth_makeauth (cur_auth, (auth_t *)from, MACH_MSG_TYPE_COPY_SEND, num_from, + ugids->eff_uids.ids, ugids->eff_uids.num, + ugids->avail_uids.ids, ugids->avail_uids.num, + ugids->eff_gids.ids, ugids->eff_gids.num, + ugids->avail_gids.ids, ugids->avail_gids.num, + auth); + mach_port_deallocate (mach_task_self (), cur_auth); + return err; +} + +/* Merge the ids from the auth port AUTH into UGIDS. */ +error_t +ugids_merge_auth (struct ugids *ugids, auth_t auth) +{ + return + idvec_merge_auth (&ugids->eff_uids, &ugids->avail_uids, + &ugids->eff_gids, &ugids->avail_gids, + auth); +} diff --git a/libshouldbeinlibc/ugids-imply.c b/libshouldbeinlibc/ugids-imply.c new file mode 100644 index 00000000..272ba664 --- /dev/null +++ b/libshouldbeinlibc/ugids-imply.c @@ -0,0 +1,36 @@ +/* Calculate implied group ids from user ids + + Copyright (C) 1997 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <errno.h> + +#include "idvec.h" +#include "ugids.h" + +/* Mark as implied all gids in UGIDS that can be implied from its uids. */ +error_t +ugids_imply_all (struct ugids *ugids) +{ + error_t err; + err = idvec_merge_implied_gids (&ugids->imp_eff_gids, &ugids->eff_uids); + if (! err) + err = + idvec_merge_implied_gids (&ugids->imp_avail_gids, &ugids->avail_uids); + return err; +} diff --git a/libshouldbeinlibc/ugids-merge.c b/libshouldbeinlibc/ugids-merge.c new file mode 100644 index 00000000..f97da07c --- /dev/null +++ b/libshouldbeinlibc/ugids-merge.c @@ -0,0 +1,110 @@ +/* Merging of ugids + + Copyright (C) 1997 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <errno.h> + +#include "idvec.h" +#include "ugids.h" + +static error_t +_merge_gids (struct idvec *gids, struct idvec *gids_imp, + const struct idvec *new, const struct idvec *new_imp) +{ + error_t err; + /* Gids that exist in both GIDS and NEW can only be implied in the result + if they are implied in both; here GIDS_STRONG and NEW_STRONG contain + those gids which shouldn't be implied in the result because they are not + in either of the sources. */ + struct idvec gids_strong = IDVEC_INIT; + struct idvec new_strong = IDVEC_INIT; + + err = idvec_set (&gids_strong, gids); + if (! err) + err = idvec_set (&new_strong, new); + if (! err) + { + idvec_subtract (&gids_strong, gids_imp); + idvec_subtract (&new_strong, new_imp); + + err = idvec_merge (gids, new); + if (! err) + { + err = idvec_merge (gids_imp, new_imp); + if (! err) + { + idvec_subtract (gids_imp, &gids_strong); + idvec_subtract (gids_imp, &new_strong); + } + } + } + + idvec_fini (&gids_strong); + idvec_fini (&new_strong); + + return err; +} + +/* Add all ids in NEW to UGIDS. */ +error_t +ugids_merge (struct ugids *ugids, const struct ugids *new) +{ + error_t err; + err = idvec_merge (&ugids->eff_uids, &new->eff_uids); + if (! err) + err = idvec_merge (&ugids->avail_uids, &new->avail_uids); + if (! err) + err = _merge_gids (&ugids->eff_gids, &ugids->imp_eff_gids, + &new->eff_gids, &new->imp_eff_gids); + if (! err) + err = _merge_gids (&ugids->avail_gids, &ugids->imp_avail_gids, + &new->avail_gids, &new->imp_avail_gids); + return err; +} + +/* Set the ids in UGIDS to those in NEW. */ +error_t +ugids_set (struct ugids *ugids, const struct ugids *new) +{ + idvec_clear (&ugids->eff_uids); + idvec_clear (&ugids->eff_gids); + idvec_clear (&ugids->avail_uids); + idvec_clear (&ugids->avail_gids); + idvec_clear (&ugids->imp_eff_gids); + idvec_clear (&ugids->imp_avail_gids); + return ugids_merge (ugids, new); +} + +/* Save any effective ids in UGIDS by merging them into the available ids, + and removing them from the effective ones. */ +error_t +ugids_save (struct ugids *ugids) +{ + error_t err = idvec_merge (&ugids->avail_uids, &ugids->eff_uids); + if (! err) + err = _merge_gids (&ugids->avail_gids, &ugids->imp_avail_gids, + &ugids->eff_gids, &ugids->imp_eff_gids); + if (! err) + { + idvec_clear (&ugids->eff_uids); + idvec_clear (&ugids->eff_gids); + idvec_clear (&ugids->imp_eff_gids); + } + return err; +} diff --git a/libshouldbeinlibc/ugids-posix.c b/libshouldbeinlibc/ugids-posix.c new file mode 100644 index 00000000..35d73e32 --- /dev/null +++ b/libshouldbeinlibc/ugids-posix.c @@ -0,0 +1,95 @@ +/* Set posix-compatible ugids + + Copyright (C) 1997 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdlib.h> + +#include "ugids.h" + +/* Install UID into UGIDS as the main user, making sure that the posix + `real' and `saved' uid slots are filled in, and similarly add all + groups to which UID belongs. */ +error_t +ugids_set_posix_user (struct ugids *ugids, uid_t uid) +{ + error_t err; + struct idvec imp_gids = IDVEC_INIT; + uid_t uids_ids[] = { uid }; + struct idvec uids = { uids_ids, 1 }; + + error_t update_real (struct idvec *avail_ids, uid_t id) + { + if (avail_ids->num == 0 + || !idvec_tail_contains (avail_ids, 1, avail_ids->ids[0])) + return idvec_insert (avail_ids, 0, id); + else + avail_ids->ids[0] = id; + return 0; + } + + idvec_merge_implied_gids (&imp_gids, &uids); + + /* Try to add UID. */ + err = idvec_insert_only (&ugids->eff_uids, 0, uid); /* Effective */ + if (! err) + err = update_real (&ugids->avail_uids, uid); /* Real */ + if (! err) + err = idvec_insert_only (&ugids->avail_uids, 1, uid); /* Saved */ + + if (!err && imp_gids.num > 0) + /* Now do the gids. */ + { + /* The main gid associated with UID (usually from /etc/passwd). */ + gid_t gid = imp_gids.ids[0]; + /* True if GID was already an available gid. */ + int gid_was_avail = idvec_contains (&ugids->avail_gids, gid); + + /* Update the implied sets for the gids: they're implied unless + they were present as non-implied gids before. Here we + remove existing effective gids from the IMP_GIDS before we + added it to the implied sets -- if some of those gids were + actually implied, they'll already be present in the implied + set. */ + idvec_subtract (&imp_gids, &ugids->eff_gids); + + /* Now add GID, as effective, real, and saved gids. */ + if (! err) /* Effective */ + err = idvec_insert_only (&ugids->eff_gids, 0, gid); + if (! err) /* Real */ + err = update_real (&ugids->avail_gids, gid); + if (! err) /* Saved */ + err = idvec_insert_only (&ugids->avail_gids, 1, gid); + + /* Mark GID as implied in the available gids unless it was already + present (in which case its implied status is already settled). */ + if (!err && !gid_was_avail) + err = idvec_add (&ugids->imp_avail_gids, gid); + + /* Add the other implied gids to the end of the effective gids. */ + if (! err) + err = idvec_merge (&ugids->eff_gids, &imp_gids); + /* And make them implied. */ + if (! err) + err = idvec_merge (&ugids->imp_eff_gids, &imp_gids); + } + + idvec_fini (&imp_gids); + + return err; +} diff --git a/libshouldbeinlibc/ugids-rep.c b/libshouldbeinlibc/ugids-rep.c new file mode 100644 index 00000000..3e6e59d5 --- /dev/null +++ b/libshouldbeinlibc/ugids-rep.c @@ -0,0 +1,118 @@ +/* String representation of ugids + + Copyright (C) 1997 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdlib.h> +#include <string.h> + +#include "ugids.h" + +/* Return a string representation of the ids in UGIDS. SHOW_VALUES and + SHOW_NAMES reflect how each id is printed (if SHOW_NAMES is true values + are used where names aren't available); if both are true, the + `VALUE(NAME)' format is used. ID_SEP, TYPE_SEP, and HDR_SEP contain the + strings that separate, respectively, multiple ids of a particular type + (default ","), the various types of ids (default ", "), and the name of + each type from its ids (default ": "). The empty string is returned for + an empty list, and 0 for an allocation error. */ +char * +ugids_rep (const struct ugids *ugids, int show_values, int show_names, + const char *id_sep, const char *type_sep, const char *hdr_sep) +{ + size_t type_sep_len, hdr_sep_len; + int first = 1; + char *rep = 0; /* Result */ + size_t len = 0; /* Total length of result. */ + char *euid_rep = 0, *egid_rep = 0, *auid_rep = 0, *agid_rep = 0; + + /* Calculate the rep for NAME, with ids IDS, returning the rep for the ids + in REP, and updates LEN to include everything needed by this type (the + length of *REP *plus* the length of NAME and any separators). True is + returned unless an allocation error occurs. */ + int type_rep (const char *name, const struct idvec *ids, int is_group, + char **rep) + { + if (ids->num > 0) + { + if (first) + first = 0; + else + len += type_sep_len; + len += strlen (name); + len += hdr_sep_len; + *rep = + (is_group ? idvec_gids_rep : idvec_uids_rep) + (ids, show_values, show_names, id_sep); + if (*rep) + len += strlen (*rep); + else + return 0; + } + return 1; + } + void add_type_rep (char **to, const char *name, const char *rep) + { + if (rep) + { + if (first) + first = 0; + else + *to = stpcpy (*to, type_sep); + *to = stpcpy (*to, name); + *to = stpcpy (*to, hdr_sep); + *to = stpcpy (*to, rep); + } + } + + if (! type_sep) + type_sep = ", "; + if (! hdr_sep) + hdr_sep = ": "; + + type_sep_len = strlen (type_sep); + hdr_sep_len = strlen (hdr_sep); + + if (type_rep ("euids", &ugids->eff_uids, 0, &euid_rep) + && type_rep ("egids", &ugids->eff_gids, 1, &egid_rep) + && type_rep ("auids", &ugids->avail_uids, 0, &auid_rep) + && type_rep ("agids", &ugids->avail_gids, 1, &agid_rep)) + { + char *p = malloc (len + 1); + if (p) + { + rep = p; + first = 1; + add_type_rep (&p, "euids", euid_rep); + add_type_rep (&p, "egids", egid_rep); + add_type_rep (&p, "auids", auid_rep); + add_type_rep (&p, "agids", agid_rep); + } + } + + if (euid_rep) + free (euid_rep); + if (egid_rep) + free (egid_rep); + if (auid_rep) + free (auid_rep); + if (agid_rep) + free (agid_rep); + + return rep; +} diff --git a/libshouldbeinlibc/ugids-subtract.c b/libshouldbeinlibc/ugids-subtract.c new file mode 100644 index 00000000..b56e397b --- /dev/null +++ b/libshouldbeinlibc/ugids-subtract.c @@ -0,0 +1,130 @@ +/* Subtract one set of user and group ids from another + + Copyright (C) 1997 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <errno.h> + +#include "idvec.h" +#include "ugids.h" + +/* Remove the gids in SUB from those in GIDS, except where they are implied + in SUB (as represented by SUB_IMP), but not in GIDS (as represented by + GIDS_IMP). */ +static +error_t _sub_gids (struct idvec *gids, struct idvec *gids_imp, + const struct idvec *sub, const struct idvec *sub_imp) +{ + error_t err; + /* What we'll remove from GIDS. */ + struct idvec delta = IDVEC_INIT; + /* Those implied ids in SUB that we *won't* remove, because they're not + also implied in GIDS. */ + struct idvec delta_suppress = IDVEC_INIT; + + err = idvec_set (&delta, sub); + if (! err) + err = idvec_set (&delta_suppress, sub_imp); + if (! err) + { + /* Don't suppress those implied ids that are implied in both. */ + idvec_subtract (&delta_suppress, gids_imp); + idvec_subtract (&delta, &delta_suppress); + + /* Actually remove the gids. */ + idvec_subtract (gids, &delta); + } + + idvec_fini (&delta); + idvec_fini (&delta_suppress); + + return err; +} + +/* Remove the in SUB from those in GIDS, except where they are implied + in SUB (as represented by SUB_IMP), but not in GIDS (as represented by + GIDS_IMP). */ +static +error_t _sub (struct idvec *uids, struct idvec *gids, struct idvec *gids_imp, + const struct idvec *sub_uids, + const struct idvec *sub_gids, const struct idvec *sub_gids_imp) +{ + error_t err; + struct idvec new_uids = IDVEC_INIT; /* The set of uids after subtraction. */ + struct idvec no_sub_gids = IDVEC_INIT; /* Gids we *don't* want to remove + from GIDS, despite what's in + SUB_GIDS. */ + struct idvec new_sub_gids = IDVEC_INIT; + struct idvec new_sub_gids_imp = IDVEC_INIT; + + err = idvec_set (&new_uids, uids); + if (! err) + err = idvec_set (&new_sub_gids, sub_gids); + if (! err) + err = idvec_set (&new_sub_gids_imp, sub_gids_imp); + if (! err) + { + idvec_subtract (&new_uids, sub_uids); + + err = idvec_merge_implied_gids (&no_sub_gids, &new_uids); + if (! err) + { + /* NO_SUB_GIDS is the intersection of implied gids in GIDS, + implied gids in SUB_GIDS, and implied gids after the subtraction + of uids -- we don't want to remove those implied gids because we + can't be sure which uids implied them (as there will be + appropriately implicative uids left after the subtraction). */ + idvec_keep (&no_sub_gids, gids_imp); + idvec_keep (&no_sub_gids, sub_gids_imp); + + /* Remove those gids we don't want to subtract. */ + idvec_subtract (&new_sub_gids, &no_sub_gids); + idvec_subtract (&new_sub_gids_imp, &no_sub_gids); + + /* Do the group subtraction. */ + err = _sub_gids (gids, gids_imp, &new_sub_gids, &new_sub_gids_imp); + if (! err) + /* Finally, if no problems, do the uid subtraction. */ + err = idvec_set (uids, &new_uids); + } + } + + idvec_fini (&new_uids); + idvec_fini (&no_sub_gids); + idvec_fini (&new_sub_gids); + idvec_fini (&new_sub_gids_imp); + + return err; +} + +/* Remove the ids in SUB from those in UGIDS. */ +error_t +ugids_subtract (struct ugids *ugids, const struct ugids *sub) +{ + error_t err = + _sub (&ugids->eff_uids, &ugids->eff_gids, &ugids->imp_eff_gids, + &sub->eff_uids, &sub->eff_gids, &sub->imp_eff_gids); + + if (! err) + /* If this second call to _sub fails, ugids will be in an inconsistent + state, but oh well. */ + err = _sub (&ugids->avail_uids, &ugids->avail_gids, &ugids->imp_avail_gids, + &sub->avail_uids, &sub->avail_gids, &sub->imp_avail_gids); + + return err; +} diff --git a/libshouldbeinlibc/ugids-verify-auth.c b/libshouldbeinlibc/ugids-verify-auth.c new file mode 100644 index 00000000..0e85b1b6 --- /dev/null +++ b/libshouldbeinlibc/ugids-verify-auth.c @@ -0,0 +1,185 @@ +/* Verify user/group passwords and authenticate accordingly + + Copyright (C) 1997, 1998 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.ai.mit.edu> + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <stdlib.h> +#include <hurd.h> + +#include <hurd/paths.h> +#include <hurd/password.h> + +#include "idvec.h" +#include "ugids.h" + +/* Accumulated information from authentication various passwords. */ +struct svma_state +{ + /* The password server. */ + file_t server; + + /* An auth port for each password that was verify by the server. */ + auth_t *auths; + size_t num_auths; +}; + +/* Append the auth ports in AUTHS, of length NUM_AUTHS, to the auth port + vector in SS, returning 0 if successful, or an error. */ +static error_t +svma_state_add_auths (struct svma_state *ss, + const auth_t *auths, size_t num_auths) +{ + auth_t *new = realloc (ss->auths, + (ss->num_auths + num_auths) * sizeof (auth_t)); + if (new) + { + ss->auths = new; + while (num_auths--) + ss->auths[ss->num_auths++] = *auths++; + return 0; + } + else + return ENOMEM; +} + +/* Get authentication from PASSWORD using the hurd password server. */ +static error_t +server_verify_make_auth (const char *password, + uid_t id, int is_group, + void *pwd_or_grp, void *hook) +{ + auth_t auth; + struct svma_state *svma_state = hook; + error_t (*check) (io_t server, uid_t id, const char *passwd, auth_t *auth) = + is_group ? password_check_group : password_check_user; + error_t err = (*check) (svma_state->server, id, password, &auth); + + if (! err) + /* PASSWORD checked out ok; the corresponding authentication is in AUTH. */ + { + err = svma_state_add_auths (svma_state, &auth, 1); + if (err) + mach_port_deallocate (mach_task_self (), auth); + } + + return err; +} + +/* Verify that we have the right to the ids in UGIDS, given that we already + possess those in HAVE_UIDS and HAVE_GIDS (asking for passwords where + necessary), and return corresponding authentication in AUTH; the auth + ports in FROM, of length NUM_FROM, are used to supplement the auth port of + the current process if necessary. 0 is returned if access should be + allowed, otherwise EINVAL if an incorrect password was entered, or an + error relating to resource failure. GETPASS_FN and GETPASS_HOOK are as + for the idvec_verify function in <idvec.h>. */ +error_t +ugids_verify_make_auth (const struct ugids *ugids, + const struct idvec *have_uids, + const struct idvec *have_gids, + char *(*getpass_fn) (const char *prompt, + uid_t id, int is_group, + void *pwd_or_grp, void *hook), + void *getpass_hook, + const auth_t *from, size_t num_from, + auth_t *auth) +{ + error_t err; + /* By default, get authentication from the password server. */ + struct svma_state svma_state; + error_t (*verify_fn) (const char *password, + uid_t id, int is_group, + void *pwd_or_grp, void *hook) + = server_verify_make_auth; + void *verify_hook = &svma_state; + + /* Try to open the hurd password server. */ + svma_state.server = file_name_lookup (_SERVERS_PASSWORD, 0, 0); + + if (svma_state.server == MACH_PORT_NULL) + /* Can't open the password server, try to use our own authority in + the traditional unix manner. */ + { + verify_fn = 0; + verify_hook = 0; + } + else + { + /* Must initialize list to empty so svma_state_add_auths works. */ + svma_state.auths = NULL; + svma_state.num_auths = 0; + } + + /* Check passwords. */ + err = ugids_verify (ugids, have_uids, have_gids, + getpass_fn, getpass_hook, verify_fn, verify_hook); + + if (! err) + { + /* The user apparently has access to all the ids, try to grant the + corresponding authentication. */ + if (verify_fn) + /* Merge the authentication we got from the password server into our + result. */ + { + if (num_from > 0) + /* Use FROM as well as the passwords to get authentication. */ + err = svma_state_add_auths (&svma_state, from, num_from); + + if (! err) + { + auth_t cur_auth = getauth (); + + err = + auth_makeauth (cur_auth, + svma_state.auths, MACH_MSG_TYPE_COPY_SEND, + svma_state.num_auths, + ugids->eff_uids.ids, ugids->eff_uids.num, + ugids->avail_uids.ids, ugids->avail_uids.num, + ugids->eff_gids.ids, ugids->eff_gids.num, + ugids->avail_gids.ids, ugids->avail_gids.num, + auth); + mach_port_deallocate (mach_task_self (), cur_auth); + + /* Avoid deallocating FROM when we clean up SVMA_STATE. */ + svma_state.num_auths -= num_from; + } + } + else + /* Try to authenticate the old fashioned way... */ + err = ugids_make_auth (ugids, from, num_from, auth); + } + + if (verify_fn) + /* Clean up any left over state. */ + { + unsigned int i; + + /* Get rid of auth ports. */ + for (i = 0; i < svma_state.num_auths; i++) + mach_port_deallocate (mach_task_self (), svma_state.auths[i]); + + /* Close password server. */ + mach_port_deallocate (mach_task_self (), svma_state.server); + + if (svma_state.num_auths > 0) + free (svma_state.auths); + } + + return err; +} diff --git a/libshouldbeinlibc/ugids-verify.c b/libshouldbeinlibc/ugids-verify.c new file mode 100644 index 00000000..5686bdf8 --- /dev/null +++ b/libshouldbeinlibc/ugids-verify.c @@ -0,0 +1,65 @@ +/* Verify user/group passwords + + Copyright (C) 1997 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <hurd.h> +#include <argp.h> + +#include "idvec.h" +#include "ugids.h" + +/* Verify that we have the right to the ids in UGIDS, given that we already + possess those in HAVE_UIDS and HAVE_GIDS, asking for passwords where + necessary. 0 is returned if access should be allowed, otherwise + EINVAL if an incorrect password was entered, or an error relating to + resource failure. The GETPASS_FN, GETPASS_HOOK, VERIFY_FN, and + VERIFY_HOOK arguments are as for the idvec_verify function (in <idvec.h>). */ +error_t +ugids_verify (const struct ugids *ugids, + const struct idvec *have_uids, const struct idvec *have_gids, + char *(*getpass_fn) (const char *prompt, + uid_t id, int is_group, + void *pwd_or_grp, void *hook), + void *getpass_hook, + error_t (*verify_fn) (const char *password, + uid_t id, int is_group, + void *pwd_or_grp, void *hook), + void *verify_hook) +{ + error_t err; + struct idvec check_uids = IDVEC_INIT; /* User-ids to verify. */ + struct idvec check_gids = IDVEC_INIT; /* group-ids to verify. */ + + err = idvec_merge (&check_uids, &ugids->eff_uids); + if (! err) + err = idvec_merge (&check_uids, &ugids->avail_uids); + if (! err) + err = idvec_merge (&check_gids, &ugids->eff_gids); + if (! err) + err = idvec_merge (&check_gids, &ugids->avail_gids); + + if (! err) + err = idvec_verify (&check_uids, &check_gids, have_uids, have_gids, + getpass_fn, getpass_hook, verify_fn, verify_hook); + + idvec_fini (&check_uids); + idvec_fini (&check_gids); + + return err; +} diff --git a/libshouldbeinlibc/ugids-xinl.c b/libshouldbeinlibc/ugids-xinl.c new file mode 100644 index 00000000..107de8b9 --- /dev/null +++ b/libshouldbeinlibc/ugids-xinl.c @@ -0,0 +1,23 @@ +/* Real definitions for extern inline functions in ugids.h + + Copyright (C) 1997 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#define UGIDS_DEFINE_EI +#include "ugids.h" diff --git a/libshouldbeinlibc/ugids.c b/libshouldbeinlibc/ugids.c new file mode 100644 index 00000000..db1ce3c8 --- /dev/null +++ b/libshouldbeinlibc/ugids.c @@ -0,0 +1,98 @@ +/* Frob user and group ids + + Copyright (C) 1997 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdlib.h> +#include <string.h> + +#include "idvec.h" +#include "ugids.h" + +/* Return a new ugids structure, or 0 if an allocation error occurs. */ +struct ugids * +make_ugids () +{ + struct ugids *u = malloc (sizeof (struct ugids)); + if (u) + bzero (u, sizeof *u); + return u; +} + +/* Add a new uid to UGIDS. If AVAIL is true, it's added to the avail uids + instead of the effective ones. */ +error_t +ugids_add_uid (struct ugids *ugids, uid_t uid, int avail) +{ + return idvec_add_new (avail ? &ugids->avail_uids : &ugids->eff_uids, uid); +} + +/* Add a new gid to UGIDS. If AVAIL is true, it's added to the avail gids + instead of the effective ones. */ +error_t +ugids_add_gid (struct ugids *ugids, gid_t gid, int avail) +{ + error_t err = + idvec_add_new (avail ? &ugids->avail_gids : &ugids->eff_gids, gid); + if (! err) + /* Since this gid is now explicit, remove it from the appropriate implied + set. */ + idvec_remove (avail ? &ugids->imp_avail_gids : &ugids->imp_eff_gids, + 0, gid); + return err; +} + +/* Add UID to UGIDS, plus any gids to which that user belongs. If AVAIL is + true, the are added to the avail gids instead of the effective ones. */ +error_t +ugids_add_user (struct ugids *ugids, uid_t uid, int avail) +{ + error_t err; + struct idvec imp_gids = IDVEC_INIT; + uid_t uids_ids[] = { uid }; + struct idvec uids = { uids_ids, 1 }; + struct idvec *gids = avail ? &ugids->avail_gids : &ugids->eff_gids; + + idvec_merge_implied_gids (&imp_gids, &uids); + + /* Now remove any gids we already know about from IMP_GIDS. For gids + that weren't in the appropriate implied set before, this will + ensure that they remain out after we merge IMP_GIDS into it, and + ones that *were*, they will remain so. */ + idvec_subtract (&imp_gids, gids); + + /* Try to add UID. */ + err = idvec_add_new (avail ? &ugids->avail_uids : &ugids->eff_uids, uid); + + if (! err) + /* Now that we've added UID, we can add appropriate implied gids. + [If this fails, UGIDS will be an inconsistent state, but things + are probably fucked anyhow] */ + err = + idvec_merge (avail ? &ugids->avail_gids : &ugids->eff_gids, + &imp_gids); + if (! err) + err = idvec_merge ((avail + ? &ugids->imp_avail_gids + : &ugids->imp_eff_gids), + &imp_gids); + + idvec_fini (&imp_gids); + + return err; +} diff --git a/libshouldbeinlibc/ugids.h b/libshouldbeinlibc/ugids.h new file mode 100644 index 00000000..5d0e1134 --- /dev/null +++ b/libshouldbeinlibc/ugids.h @@ -0,0 +1,231 @@ +/* Uid/gid parsing/frobbing + + Copyright (C) 1997,2001 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef __UGIDS_H__ +#define __UGIDS_H__ + +#include <stdlib.h> /* For inline function stuff. */ +#include <idvec.h> +#include <features.h> +#include <errno.h> +#include <sys/types.h> + +#ifdef UGIDS_DEFINE_EI +#define UGIDS_EI +#else +#define UGIDS_EI __extern_inline +#endif + +/* A structure holding a set of the common various types of ids. */ +struct ugids +{ + struct idvec eff_uids; /* Effective UIDs */ + struct idvec eff_gids; /* Effective GIDs */ + struct idvec avail_uids; /* Available UIDs */ + struct idvec avail_gids; /* Available GIDs */ + + /* These should be a subset of EFF/AVAIL_GIDS, containing those gids which + are present only by implication from uids in EFF/AVAIL_UIDS. */ + struct idvec imp_eff_gids; + struct idvec imp_avail_gids; +}; + +#define UGIDS_INIT { IDVEC_INIT, IDVEC_INIT, IDVEC_INIT, IDVEC_INIT, IDVEC_INIT, IDVEC_INIT } + +/* Return a new ugids structure, or 0 if an allocation error occurs. */ +struct ugids *make_ugids (); + +extern void ugids_fini (struct ugids *ugids); + +extern void ugids_free (struct ugids *ugids); + +extern int ugids_is_empty (const struct ugids *ugids); + +extern int ugids_equal (const struct ugids *ugids1, const struct ugids *ugids2); + +#if defined(__USE_EXTERN_INLINES) || defined(UGIDS_DEFINE_EI) + +/* Free all resources used by UGIDS except UGIDS itself. */ +UGIDS_EI void +ugids_fini (struct ugids *ugids) +{ + idvec_fini (&ugids->eff_uids); + idvec_fini (&ugids->eff_gids); + idvec_fini (&ugids->avail_uids); + idvec_fini (&ugids->avail_gids); + idvec_fini (&ugids->imp_eff_gids); + idvec_fini (&ugids->imp_avail_gids); +} + +/* Free all resources used by UGIDS. */ +UGIDS_EI void +ugids_free (struct ugids *ugids) +{ + ugids_fini (ugids); + free (ugids); +} + +/* Return true if UGIDS contains no ids. */ +UGIDS_EI int +ugids_is_empty (const struct ugids *ugids) +{ + /* We needn't test the imp_*_gids vectors because they are subsets of the + corresponding *_gids vectors. */ + return + idvec_is_empty (&ugids->eff_uids) + && idvec_is_empty (&ugids->eff_gids) + && idvec_is_empty (&ugids->avail_uids) + && idvec_is_empty (&ugids->avail_gids); +} + +/* Free all resources used by UGIDS except UGIDS itself. */ +UGIDS_EI int +ugids_equal (const struct ugids *ugids1, const struct ugids *ugids2) +{ + return + idvec_equal (&ugids1->eff_uids, &ugids2->eff_uids) + && idvec_equal (&ugids1->eff_gids, &ugids2->eff_gids) + && idvec_equal (&ugids1->avail_uids, &ugids2->avail_uids) + && idvec_equal (&ugids1->avail_gids, &ugids2->avail_gids) + && idvec_equal (&ugids1->imp_eff_gids, &ugids2->imp_eff_gids) + && idvec_equal (&ugids1->imp_avail_gids, &ugids2->imp_avail_gids); +} + +#endif /* Use extern inlines. */ + +/* Add all ids in NEW to UGIDS. */ +error_t ugids_merge (struct ugids *ugids, const struct ugids *new); + +/* Set the ids in UGIDS to those in NEW. */ +error_t ugids_set (struct ugids *ugids, const struct ugids *new); + +/* Remove the ids in SUB from those in UGIDS. */ +error_t ugids_subtract (struct ugids *ugids, const struct ugids *sub); + +/* Mark as implied all gids in UGIDS that can be implied from its uids. */ +error_t ugids_imply_all (struct ugids *ugids); + +/* Save any effective ids in UGIDS by merging them into the available ids, + and removing them from the effective ones. */ +error_t ugids_save (struct ugids *ugids); + +/* Verify that we have the right to the ids in UGIDS, given that we already + possess those in HAVE_UIDS and HAVE_GIDS, asking for passwords where + necessary. 0 is returned if access should be allowed, otherwise + EINVAL if an incorrect password was entered, or an error relating to + resource failure. The GETPASS_FN, GETPASS_HOOK, VERIFY_FN, and + VERIFY_HOOK arguments are as for the idvec_verify function (in <idvec.h>). */ +error_t ugids_verify (const struct ugids *ugids, + const struct idvec *have_uids, + const struct idvec *have_gids, + char *(*getpass_fn) (const char *prompt, + uid_t id, int is_group, + void *pwd_or_grp, void *hook), + void *getpass_hook, + error_t (*verify_fn) (const char *password, + uid_t id, int is_group, + void *pwd_or_grp, void *hook), + void *verify_hook); + +/* Make an auth port from UGIDS and return it in AUTH, using authority in + both the auth port FROM and the current auth port. */ +error_t ugids_make_auth (const struct ugids *ugids, + const auth_t *from, size_t num_from, + auth_t *auth); + +/* Verify that we have the right to the ids in UGIDS, given that we already + possess those in HAVE_UIDS and HAVE_GIDS (asking for passwords where + necessary), and return corresponding authentication in AUTH; the auth + ports in FROM, of length NUM_FROM, are used to supplement the auth port of + the current process if necessary. 0 is returned if access should be + allowed, otherwise EINVAL if an incorrect password was entered, or an + error relating to resource failure. GETPASS_FN and GETPASS_HOOK are as + for the idvec_verify function in <idvec.h>. */ +error_t ugids_verify_make_auth (const struct ugids *ugids, + const struct idvec *have_uids, + const struct idvec *have_gids, + char *(*getpass_fn) (const char *prompt, + uid_t id, int is_group, + void *pwd_or_grp, + void *hook), + void *getpass_hook, + const auth_t *from, size_t num_from, + auth_t *auth); + +/* Merge the ids from the auth port AUTH into UGIDS. */ +error_t ugids_merge_auth (struct ugids *ugids, auth_t auth); + +/* Return a string representation of the ids in UGIDS. SHOW_VALUES and + SHOW_NAMES reflect how each id is printed (if SHOW_NAMES is true values + are used where names aren't available); if both are true, the + `VALUE(NAME)' format is used. ID_SEP, TYPE_SEP, and HDR_SEP contain the + strings that separate, respectively, multiple ids of a particular type + (default ","), the various types of ids (default ", "), and the name of + each type from its ids (default ": "). The empty string is returned for + an empty list, and 0 for an allocation error. */ +char *ugids_rep (const struct ugids *ugids, int show_values, int show_names, + const char *id_sep, const char *type_sep, + const char *hdr_sep); + +/* Add a new uid to UGIDS. If AVAIL is true, it's added to the avail uids + instead of the effective ones. */ +error_t ugids_add_uid (struct ugids *ugids, uid_t uid, int avail); + +/* Add a new gid to UGIDS. If AVAIL is true, it's added to the avail gids + instead of the effective ones. */ +error_t ugids_add_gid (struct ugids *ugids, gid_t gid, int avail); + +/* Add UID to UGIDS, plus any gids to which that user belongs. If AVAIL is + true, the are added to the avail gids instead of the effective ones. */ +error_t ugids_add_user (struct ugids *ugids, uid_t uid, int avail); + +/* Install UID into UGIDS as the main user, making sure that the posix + `real' and `saved' uid slots are filled in, and similarly add all + groups to which UID belongs. */ +error_t ugids_set_posix_user (struct ugids *ugids, uid_t uid); + +/* Params to be passed as the input when parsing UGIDS_ARGP. */ +struct ugids_argp_params +{ + /* Parsed ids should be added here. */ + struct ugids *ugids; + + /* If true, parse multiple args as user otherwise, parse none. */ + int parse_user_args; + + /* If true, and PARSE_USER_ARGS is true, add user args to the available + ids, not the effective ones. If both are true, add them to both. + If both are false, use the special ugids_set_posix_user instead (which + sets both, in a particular way). */ + int user_args_are_effective; + int user_args_are_available; + + /* If >= 0, a user that should be added if none are specified on the + command line (following the same rules). */ + int default_user; + + /* If true, at least one id has to be specified. */ + int require_ids; +}; + +/* A parser for selecting a set of ugids. */ +extern struct argp ugids_argp; + +#endif /* __UGIDS_H__ */ diff --git a/libshouldbeinlibc/wire.c b/libshouldbeinlibc/wire.c new file mode 100644 index 00000000..ca5d32b1 --- /dev/null +++ b/libshouldbeinlibc/wire.c @@ -0,0 +1,186 @@ +/* Function to wire down text and data (including from shared libraries) + Copyright (C) 1996,99,2000,01,02 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + + +#include <link.h> +#include <dlfcn.h> +#include <hurd.h> +#include <error.h> +#include <elf.h> + +#pragma weak _DYNAMIC +#pragma weak dlopen +#pragma weak dlclose +#pragma weak dlerror +#pragma weak dlsym +#ifndef RTLD_NOLOAD +#define RTLD_NOLOAD 0 +#endif + +/* Find the list of shared objects */ +static struct link_map * +loaded (void) +{ + ElfW(Dyn) *d; + + if (&_DYNAMIC == 0) /* statically linked */ + return 0; + + for (d = _DYNAMIC; d->d_tag != DT_NULL; ++d) + if (d->d_tag == DT_DEBUG) + { + struct r_debug *r = (void *) d->d_un.d_ptr; + return r->r_map; + } + + return 0; /* ld broken */ +} + +/* Compute the extent of a particular shared object. */ +static ElfW(Addr) +map_extent (struct link_map *map) +{ + /* In fact, LIB == MAP, but doing it this way makes it entirely kosher. */ + void *lib = dlopen (map->l_name, RTLD_NOLOAD); + if (lib == 0) + { + error (2, 0, "cannot dlopen %s: %s", map->l_name, dlerror ()); + /* NOTREACHED */ + return 0; + } + else + { + /* Find the _end symbol's runtime address and subtract the load base. */ + void *end = dlsym (lib, "_end"); + if (end == 0) + error (2, 0, "cannot wire library %s with no _end symbol: %s", + map->l_name, dlerror ()); + dlclose (lib); + return (ElfW(Addr)) end - map->l_addr; + } +} + +/* Wire down all memory currently allocated at START for LEN bytes; + host_priv is the privileged host port. */ +static void +wire_segment_internal (vm_address_t start, + vm_size_t len, + host_priv_t host_priv) +{ + vm_address_t addr; + vm_size_t size; + vm_prot_t protection; + vm_prot_t max_protection; + vm_inherit_t inheritance; + boolean_t shared; + mach_port_t object_name; + vm_offset_t offset; + error_t err; + volatile char *poke; + + do + { + addr = start; + err = vm_region (mach_task_self (), &addr, &size, &protection, + &max_protection, &inheritance, &shared, &object_name, + &offset); + if (err) + return; + + /* The current region begins at ADDR and is SIZE long. If it + extends beyond the LEN, prune it. */ + if (addr + size > start + len) + size = len - (addr - start); + + /* Set protection to allow all access possible */ + vm_protect (mach_task_self (), addr, size, 0, max_protection); + + /* Generate write faults */ + for (poke = (char *) addr; + (vm_address_t) poke < addr + size; + poke += vm_page_size) + *poke = *poke; + + /* Wire pages */ + vm_wire (host_priv, mach_task_self (), addr, size, max_protection); + + /* Set protection back to what it was */ + vm_protect (mach_task_self (), addr, size, 0, protection); + + + mach_port_deallocate (mach_task_self (), object_name); + + len -= (addr - start) + size; + start = addr + size; + } + while (len); +} + +/* Wire down all memory currently allocated at START for LEN bytes. */ +void +wire_segment (vm_address_t start, + vm_size_t len) +{ + mach_port_t host, device; + error_t err; + + err = get_privileged_ports (&host, &device); + if (!err) + { + wire_segment_internal (start, len, host); + mach_port_deallocate (mach_task_self (), host); + mach_port_deallocate (mach_task_self (), device); + } +} + + +/* Wire down all the text and data (including from shared libraries) + for the current program. */ +void +wire_task_self () +{ + struct link_map *map; + mach_port_t host, device; + error_t err; + extern char _edata, _etext, __data_start; + + err = get_privileged_ports (&host, &device); + if (err) + return; + + map = loaded (); + if (!map) + { + extern void _start (); + vm_address_t text_start = (vm_address_t) &_start; + wire_segment_internal (text_start, + (vm_size_t) (&_etext - text_start), + host); + wire_segment_internal ((vm_address_t) &__data_start, + (vm_size_t) (&_edata - &__data_start), + host); + } + else + while (map) + wire_segment ((vm_address_t) map->l_addr, map_extent (map)); + + mach_port_deallocate (mach_task_self (), host); + mach_port_deallocate (mach_task_self (), device); +} diff --git a/libshouldbeinlibc/wire.h b/libshouldbeinlibc/wire.h new file mode 100644 index 00000000..6783cc50 --- /dev/null +++ b/libshouldbeinlibc/wire.h @@ -0,0 +1,26 @@ +/* Function to wire down text and data (including from shared libraries) + Copyright (C) 1996 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +/* Wire down all the text and data (including from shared libraries) + for the current program. */ +void wire_task_self (void); + +/* Wire down all memory currently allocated at START for LEN bytes. */ +void wire_segment (vm_address_t start, vm_size_t len); diff --git a/libshouldbeinlibc/xportinfo.c b/libshouldbeinlibc/xportinfo.c new file mode 100644 index 00000000..cce6fb6c --- /dev/null +++ b/libshouldbeinlibc/xportinfo.c @@ -0,0 +1,69 @@ +/* Print information about a port, with the name translated between tasks + + Copyright (C) 1996, 1999 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <sys/types.h> +#include <sys/mman.h> + +#include "portinfo.h" + +/* Prints info about NAME translated through X to STREAM, in a way described + by the flags in SHOW. If TYPE is non-zero, it should be what + mach_port_type returns for NAME in X->to_task. */ +error_t +print_xlated_port_info (mach_port_t name, mach_port_type_t type, + struct port_name_xlator *x, + unsigned show, FILE *stream) +{ + mach_port_t old_name = name; + error_t err = port_name_xlator_xlate (x, name, type, &name, &type); + if (! err) + { + fprintf (stream, (show & PORTINFO_HEX_NAMES) ? "%#6zx => " : "%6zd => ", + old_name); + err = print_port_info (name, type, x->to_task, show, stream); + } + return err; +} + +/* Prints info about every port common to both tasks in X, but only if the + port in X->from_task has a type in ONLY, to STREAM. */ +error_t +print_xlated_task_ports_info (struct port_name_xlator *x, + mach_port_type_t only, + unsigned show, FILE *stream) +{ + mach_port_t *names = 0; + mach_port_type_t *types = 0; + mach_msg_type_number_t names_len = 0, types_len = 0, i; + error_t err = + mach_port_names (x->from_task, &names, &names_len, &types, &types_len); + + if (err) + return err; + + for (i = 0; i < names_len; i++) + if (types[i] & only) + print_xlated_port_info (names[i], types[i], x, show, stream); + + munmap ((caddr_t) names, names_len * sizeof *names); + munmap ((caddr_t) types, types_len * sizeof *types); + + return 0; +} |