summaryrefslogtreecommitdiffstats
path: root/lib/gshadow.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gshadow.c')
-rw-r--r--lib/gshadow.c506
1 files changed, 506 insertions, 0 deletions
diff --git a/lib/gshadow.c b/lib/gshadow.c
new file mode 100644
index 0000000..2e12923
--- /dev/null
+++ b/lib/gshadow.c
@@ -0,0 +1,506 @@
+/*
+ * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh
+ * SPDX-FileCopyrightText: 1996 - 1998, Marek Michałkiewicz
+ * SPDX-FileCopyrightText: 2005 , Tomasz Kłoczko
+ * SPDX-FileCopyrightText: 2008 - 2009, Nicolas François
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <config.h>
+
+/* Newer versions of Linux libc already have shadow support. */
+#if defined(SHADOWGRP) && !defined(HAVE_SHADOWGRP) /*{ */
+
+#ident "$Id$"
+
+#include <stdio.h>
+#include "prototypes.h"
+#include "defines.h"
+static /*@null@*/FILE *shadow;
+static /*@null@*//*@only@*/char **members = NULL;
+static size_t nmembers = 0;
+static /*@null@*//*@only@*/char **admins = NULL;
+static size_t nadmins = 0;
+static struct sgrp sgroup;
+
+#define FIELDS 4
+
+#ifdef USE_NIS
+static bool nis_used;
+static bool nis_ignore;
+static enum { native, start, middle, native2 } nis_state;
+static bool nis_bound;
+static char *nis_domain;
+static char *nis_key;
+static int nis_keylen;
+static char *nis_val;
+static int nis_vallen;
+
+#define IS_NISCHAR(c) ((c)=='+')
+#endif
+
+#ifdef USE_NIS
+/*
+ * bind_nis - bind to NIS server
+ */
+
+static int bind_nis (void)
+{
+ if (yp_get_default_domain (&nis_domain))
+ return -1;
+
+ nis_bound = true;
+ return 0;
+}
+#endif
+
+static /*@null@*/char **build_list (char *s, char **list[], size_t * nlist)
+{
+ char **ptr = *list;
+ size_t nelem = *nlist, size;
+
+ while (s != NULL && *s != '\0') {
+ size = (nelem + 1) * sizeof (ptr);
+ ptr = realloc (*list, size);
+ if (NULL != ptr) {
+ ptr[nelem] = s;
+ nelem++;
+ *list = ptr;
+ *nlist = nelem;
+ s = strchr (s, ',');
+ if (NULL != s) {
+ *s = '\0';
+ s++;
+ }
+ }
+ }
+ size = (nelem + 1) * sizeof (ptr);
+ ptr = realloc (*list, size);
+ if (NULL != ptr) {
+ ptr[nelem] = NULL;
+ *list = ptr;
+ }
+ return ptr;
+}
+
+void setsgent (void)
+{
+#ifdef USE_NIS
+ nis_state = native;
+#endif
+ if (NULL != shadow) {
+ rewind (shadow);
+ } else {
+ shadow = fopen (SGROUP_FILE, "r");
+ }
+}
+
+void endsgent (void)
+{
+ if (NULL != shadow) {
+ (void) fclose (shadow);
+ }
+
+ shadow = (FILE *) 0;
+}
+
+/*@observer@*//*@null@*/struct sgrp *sgetsgent (const char *string)
+{
+ static char *sgrbuf = NULL;
+ static size_t sgrbuflen = 0;
+
+ char *fields[FIELDS];
+ char *cp;
+ int i;
+ size_t len = strlen (string) + 1;
+
+ if (len > sgrbuflen) {
+ char *buf = (char *) realloc (sgrbuf, sizeof (char) * len);
+ if (NULL == buf) {
+ return NULL;
+ }
+ sgrbuf = buf;
+ sgrbuflen = len;
+ }
+
+ strncpy (sgrbuf, string, len);
+ sgrbuf[len-1] = '\0';
+
+ cp = strrchr (sgrbuf, '\n');
+ if (NULL != cp) {
+ *cp = '\0';
+ }
+
+ /*
+ * There should be exactly 4 colon separated fields. Find
+ * all 4 of them and save the starting addresses in fields[].
+ */
+
+ for (cp = sgrbuf, i = 0; (i < FIELDS) && (NULL != cp); i++) {
+ fields[i] = cp;
+ cp = strchr (cp, ':');
+ if (NULL != cp) {
+ *cp++ = '\0';
+ }
+ }
+
+ /*
+ * If there was an extra field somehow, or perhaps not enough,
+ * the line is invalid.
+ */
+
+ if ((NULL != cp) || (i != FIELDS)) {
+#ifdef USE_NIS
+ if (!IS_NISCHAR (fields[0][0])) {
+ return 0;
+ } else {
+ nis_used = true;
+ }
+#else
+ return 0;
+#endif
+ }
+
+ sgroup.sg_name = fields[0];
+ sgroup.sg_passwd = fields[1];
+ if (0 != nadmins) {
+ nadmins = 0;
+ free (admins);
+ admins = NULL;
+ }
+ if (0 != nmembers) {
+ nmembers = 0;
+ free (members);
+ members = NULL;
+ }
+ sgroup.sg_adm = build_list (fields[2], &admins, &nadmins);
+ sgroup.sg_mem = build_list (fields[3], &members, &nmembers);
+
+ return &sgroup;
+}
+
+/*
+ * fgetsgent - convert next line in stream to (struct sgrp)
+ *
+ * fgetsgent() reads the next line from the provided stream and
+ * converts it to a (struct sgrp). NULL is returned on EOF.
+ */
+
+/*@observer@*//*@null@*/struct sgrp *fgetsgent (/*@null@*/FILE * fp)
+{
+ static size_t buflen = 0;
+ static char *buf = NULL;
+
+ char *cp;
+
+ if (0 == buflen) {
+ buf = (char *) malloc (BUFSIZ);
+ if (NULL == buf) {
+ return NULL;
+ }
+ buflen = BUFSIZ;
+ }
+
+ if (NULL == fp) {
+ return NULL;
+ }
+
+#ifdef USE_NIS
+ while (fgetsx (buf, (int) buflen, fp) == buf)
+#else
+ if (fgetsx (buf, (int) buflen, fp) == buf)
+#endif
+ {
+ while ( ((cp = strrchr (buf, '\n')) == NULL)
+ && (feof (fp) == 0)) {
+ size_t len;
+
+ cp = (char *) realloc (buf, buflen*2);
+ if (NULL == cp) {
+ return NULL;
+ }
+ buf = cp;
+ buflen *= 2;
+
+ len = strlen (buf);
+ if (fgetsx (&buf[len],
+ (int) (buflen - len),
+ fp) != &buf[len]) {
+ return NULL;
+ }
+ }
+ cp = strrchr (buf, '\n');
+ if (NULL != cp) {
+ *cp = '\0';
+ }
+#ifdef USE_NIS
+ if (nis_ignore && IS_NISCHAR (buf[0])) {
+ continue;
+ }
+#endif
+ return (sgetsgent (buf));
+ }
+ return NULL;
+}
+
+/*
+ * getsgent - get a single shadow group entry
+ */
+
+/*@observer@*//*@null@*/struct sgrp *getsgent (void)
+{
+#ifdef USE_NIS
+ bool nis_1_group = false;
+ struct sgrp *val;
+#endif
+ if (NULL == shadow) {
+ setsgent ();
+ }
+
+#ifdef USE_NIS
+ again:
+ /*
+ * See if we are reading from the local file.
+ */
+
+ if (nis_state == native || nis_state == native2) {
+
+ /*
+ * Get the next entry from the shadow group file. Return
+ * NULL right away if there is none.
+ */
+
+ val = fgetsgent (shadow);
+ if (NULL == val) {
+ return 0;
+ }
+
+ /*
+ * If this entry began with a NIS escape character, we have
+ * to see if this is just a single group, or if the entire
+ * map is being asked for.
+ */
+
+ if (IS_NISCHAR (val->sg_name[0])) {
+ if ('\0' != val->sg_name[1]) {
+ nis_1_group = true;
+ } else {
+ nis_state = start;
+ }
+ }
+
+ /*
+ * If this isn't a NIS group and this isn't an escape to go
+ * use a NIS map, it must be a regular local group.
+ */
+
+ if (!nis_1_group && (nis_state != start)) {
+ return val;
+ }
+
+ /*
+ * If this is an escape to use an NIS map, switch over to
+ * that bunch of code.
+ */
+
+ if (nis_state == start) {
+ goto again;
+ }
+
+ /*
+ * NEEDSWORK. Here we substitute pieces-parts of this entry.
+ */
+
+ return 0;
+ } else {
+ if (!nis_bound) {
+ if (bind_nis ()) {
+ nis_state = native2;
+ goto again;
+ }
+ }
+ if (nis_state == start) {
+ if (yp_first (nis_domain, "gshadow.byname", &nis_key,
+ &nis_keylen, &nis_val, &nis_vallen)) {
+ nis_state = native2;
+ goto again;
+ }
+ nis_state = middle;
+ } else if (nis_state == middle) {
+ if (yp_next (nis_domain, "gshadow.byname", nis_key,
+ nis_keylen, &nis_key, &nis_keylen,
+ &nis_val, &nis_vallen)) {
+ nis_state = native2;
+ goto again;
+ }
+ }
+ return sgetsgent (nis_val);
+ }
+#else
+ return (fgetsgent (shadow));
+#endif
+}
+
+/*
+ * getsgnam - get a shadow group entry by name
+ */
+
+/*@observer@*//*@null@*/struct sgrp *getsgnam (const char *name)
+{
+ struct sgrp *sgrp;
+
+#ifdef USE_NIS
+ static char save_name[16];
+ int nis_disabled = 0;
+#endif
+
+ setsgent ();
+
+#ifdef USE_NIS
+ if (nis_used) {
+ again:
+
+ /*
+ * Search the gshadow.byname map for this group.
+ */
+
+ if (!nis_bound) {
+ bind_nis ();
+ }
+
+ if (nis_bound) {
+ char *cp;
+
+ if (yp_match (nis_domain, "gshadow.byname", name,
+ strlen (name), &nis_val,
+ &nis_vallen) == 0) {
+ cp = strchr (nis_val, '\n');
+ if (NULL != cp) {
+ *cp = '\0';
+ }
+
+ nis_state = middle;
+ sgrp = sgetsgent (nis_val);
+ if (NULL != sgrp) {
+ strcpy (save_name, sgrp->sg_name);
+ nis_key = save_name;
+ nis_keylen = strlen (save_name);
+ }
+ return sgrp;
+ }
+ }
+ nis_state = native2;
+ }
+#endif
+#ifdef USE_NIS
+ if (nis_used) {
+ nis_ignore = true;
+ nis_disabled = true;
+ }
+#endif
+ while ((sgrp = getsgent ()) != (struct sgrp *) 0) {
+ if (strcmp (name, sgrp->sg_name) == 0) {
+ break;
+ }
+ }
+#ifdef USE_NIS
+ nis_ignore = false;
+#endif
+ return sgrp;
+}
+
+/*
+ * putsgent - output shadow group entry in text form
+ *
+ * putsgent() converts the contents of a (struct sgrp) to text and
+ * writes the result to the given stream. This is the logical
+ * opposite of fgetsgent.
+ */
+
+int putsgent (const struct sgrp *sgrp, FILE * fp)
+{
+ char *buf, *cp;
+ int i;
+ size_t size;
+
+ if ((NULL == fp) || (NULL == sgrp)) {
+ return -1;
+ }
+
+ /* calculate the required buffer size */
+ size = strlen (sgrp->sg_name) + strlen (sgrp->sg_passwd) + 10;
+ for (i = 0; (NULL != sgrp->sg_adm) && (NULL != sgrp->sg_adm[i]); i++) {
+ size += strlen (sgrp->sg_adm[i]) + 1;
+ }
+ for (i = 0; (NULL != sgrp->sg_mem) && (NULL != sgrp->sg_mem[i]); i++) {
+ size += strlen (sgrp->sg_mem[i]) + 1;
+ }
+
+ buf = malloc (size);
+ if (NULL == buf) {
+ return -1;
+ }
+ cp = buf;
+
+ /*
+ * Copy the group name and passwd.
+ */
+
+ strcpy (cp, sgrp->sg_name);
+ cp += strlen (cp);
+ *cp++ = ':';
+
+ strcpy (cp, sgrp->sg_passwd);
+ cp += strlen (cp);
+ *cp++ = ':';
+
+ /*
+ * Copy the administrators, separating each from the other
+ * with a ",".
+ */
+
+ for (i = 0; NULL != sgrp->sg_adm[i]; i++) {
+ if (i > 0) {
+ *cp++ = ',';
+ }
+
+ strcpy (cp, sgrp->sg_adm[i]);
+ cp += strlen (cp);
+ }
+ *cp = ':';
+ cp++;
+
+ /*
+ * Now do likewise with the group members.
+ */
+
+ for (i = 0; NULL != sgrp->sg_mem[i]; i++) {
+ if (i > 0) {
+ *cp = ',';
+ cp++;
+ }
+
+ strcpy (cp, sgrp->sg_mem[i]);
+ cp += strlen (cp);
+ }
+ *cp = '\n';
+ cp++;
+ *cp = '\0';
+
+ /*
+ * Output using the function which understands the line
+ * continuation conventions.
+ */
+
+ if (fputsx (buf, fp) == EOF) {
+ free (buf);
+ return -1;
+ }
+
+ free (buf);
+ return 0;
+}
+#else
+extern int errno; /* warning: ANSI C forbids an empty source file */
+#endif /*} SHADOWGRP */