summaryrefslogtreecommitdiff
path: root/usr/src/lib/nsswitch/files/common/files_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/nsswitch/files/common/files_common.c')
-rw-r--r--usr/src/lib/nsswitch/files/common/files_common.c606
1 files changed, 606 insertions, 0 deletions
diff --git a/usr/src/lib/nsswitch/files/common/files_common.c b/usr/src/lib/nsswitch/files/common/files_common.c
new file mode 100644
index 0000000000..2a8c30bc43
--- /dev/null
+++ b/usr/src/lib/nsswitch/files/common/files_common.c
@@ -0,0 +1,606 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1995-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Common code and structures used by name-service-switch "files" backends.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * An implementation that used mmap() sensibly would be a wonderful thing,
+ * but this here is just yer standard fgets() thang.
+ */
+
+#include "files_common.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/*ARGSUSED*/
+nss_status_t
+_nss_files_setent(be, dummy)
+ files_backend_ptr_t be;
+ void *dummy;
+{
+ if (be->f == 0) {
+ if (be->filename == 0) {
+ /* Backend isn't initialized properly? */
+ return (NSS_UNAVAIL);
+ }
+ if ((be->f = __nsl_fopen(be->filename, "r")) == 0) {
+ return (NSS_UNAVAIL);
+ }
+ } else {
+ __nsl_rewind(be->f);
+ }
+ return (NSS_SUCCESS);
+}
+
+/*ARGSUSED*/
+nss_status_t
+_nss_files_endent(be, dummy)
+ files_backend_ptr_t be;
+ void *dummy;
+{
+ if (be->f != 0) {
+ __nsl_fclose(be->f);
+ be->f = 0;
+ }
+ if (be->buf != 0) {
+ free(be->buf);
+ be->buf = 0;
+ }
+ return (NSS_SUCCESS);
+}
+
+/*
+ * This routine reads a line, including the processing of continuation
+ * characters. It always leaves (or inserts) \n\0 at the end of the line.
+ * It returns the length of the line read, excluding the \n\0. Who's idea
+ * was this?
+ * Returns -1 on EOF.
+ *
+ * Note that since each concurrent call to _nss_files_read_line has
+ * it's own FILE pointer, we can use getc_unlocked w/o difficulties,
+ * a substantial performance win.
+ */
+int
+_nss_files_read_line(f, buffer, buflen)
+ __NSL_FILE *f;
+ char *buffer;
+ int buflen;
+{
+ int linelen; /* 1st unused slot in buffer */
+ int c;
+
+ /*CONSTCOND*/
+ while (1) {
+ linelen = 0;
+ while (linelen < buflen - 1) { /* "- 1" saves room for \n\0 */
+ switch (c = __nsl_getc_unlocked(f)) {
+ case EOF:
+ if (linelen == 0 ||
+ buffer[linelen - 1] == '\\') {
+ return (-1);
+ } else {
+ buffer[linelen ] = '\n';
+ buffer[linelen + 1] = '\0';
+ return (linelen);
+ }
+ case '\n':
+ if (linelen > 0 &&
+ buffer[linelen - 1] == '\\') {
+ --linelen; /* remove the '\\' */
+ } else {
+ buffer[linelen ] = '\n';
+ buffer[linelen + 1] = '\0';
+ return (linelen);
+ }
+ break;
+ default:
+ buffer[linelen++] = c;
+ }
+ }
+ /* Buffer overflow -- eat rest of line and loop again */
+ /* ===> Should syslog() */
+ do {
+ c = __nsl_getc_unlocked(f);
+ if (c == EOF) {
+ return (-1);
+ }
+ } while (c != '\n');
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * used only for getgroupbymem() now.
+ */
+nss_status_t
+_nss_files_do_all(be, args, filter, func)
+ files_backend_ptr_t be;
+ void *args;
+ const char *filter;
+ files_do_all_func_t func;
+{
+ char *buffer;
+ int buflen;
+ nss_status_t res;
+
+ if (be->buf == 0 &&
+ (be->buf = malloc(be->minbuf)) == 0) {
+ return (NSS_UNAVAIL);
+ }
+ buffer = be->buf;
+ buflen = be->minbuf;
+
+ if ((res = _nss_files_setent(be, 0)) != NSS_SUCCESS) {
+ return (res);
+ }
+
+ res = NSS_NOTFOUND;
+
+ do {
+ int linelen;
+
+ if ((linelen = _nss_files_read_line(be->f, buffer,
+ buflen)) < 0) {
+ /* End of file */
+ break;
+ }
+ if (filter != 0 && strstr(buffer, filter) == 0) {
+ /*
+ * Optimization: if the entry doesn't contain the
+ * filter string then it can't be the entry we want,
+ * so don't bother looking more closely at it.
+ */
+ continue;
+ }
+ res = (*func)(buffer, linelen, args);
+
+ } while (res == NSS_NOTFOUND);
+
+ _nss_files_endent(be, 0);
+ return (res);
+}
+
+/*
+ * Could implement this as an iterator function on top of _nss_files_do_all(),
+ * but the shared code is small enough that it'd be pretty silly.
+ */
+nss_status_t
+_nss_files_XY_all(be, args, netdb, filter, check)
+ files_backend_ptr_t be;
+ nss_XbyY_args_t *args;
+ int netdb; /* whether it uses netdb */
+ /* format or not */
+ const char *filter; /* advisory, to speed up */
+ /* string search */
+ files_XY_check_func check; /* NULL means one-shot, for getXXent */
+{
+ nss_status_t res;
+ int parsestat;
+ int (*func)();
+
+ if (be->buf == 0 &&
+ (be->buf = malloc(be->minbuf)) == 0) {
+ return (NSS_UNAVAIL); /* really panic, malloc failed */
+ }
+
+ if (check != 0 || be->f == 0) {
+ if ((res = _nss_files_setent(be, 0)) != NSS_SUCCESS) {
+ return (res);
+ }
+ }
+
+ res = NSS_NOTFOUND;
+
+ /*CONSTCOND*/
+ while (1) {
+ char *instr = be->buf;
+ int linelen;
+
+ if ((linelen = _nss_files_read_line(be->f, instr,
+ be->minbuf)) < 0) {
+ /* End of file */
+ args->returnval = 0;
+ args->erange = 0;
+ break;
+ }
+ if (filter != 0 && strstr(instr, filter) == 0) {
+ /*
+ * Optimization: if the entry doesn't contain the
+ * filter string then it can't be the entry we want,
+ * so don't bother looking more closely at it.
+ */
+ continue;
+ }
+ if (netdb) {
+ char *first;
+ char *last;
+
+ if ((last = strchr(instr, '#')) == 0) {
+ last = instr + linelen;
+ }
+ *last-- = '\0'; /* Nuke '\n' or #comment */
+
+ /*
+ * Skip leading whitespace. Normally there isn't
+ * any, so it's not worth calling strspn().
+ */
+ for (first = instr; isspace(*first); first++) {
+ ;
+ }
+ if (*first == '\0') {
+ continue;
+ }
+ /*
+ * Found something non-blank on the line. Skip back
+ * over any trailing whitespace; since we know
+ * there's non-whitespace earlier in the line,
+ * checking for termination is easy.
+ */
+ while (isspace(*last)) {
+ --last;
+ }
+
+ linelen = last - first + 1;
+ if (first != instr) {
+ instr = first;
+ }
+ }
+
+ args->returnval = 0;
+
+ func = args->str2ent;
+ parsestat = (*func)(instr, linelen, args->buf.result,
+ args->buf.buffer, args->buf.buflen);
+
+ if (parsestat == NSS_STR_PARSE_SUCCESS) {
+ args->returnval = args->buf.result;
+ if (check == 0 || (*check)(args)) {
+ res = NSS_SUCCESS;
+ break;
+ }
+ } else if (parsestat == NSS_STR_PARSE_ERANGE) {
+ args->erange = 1;
+ break;
+ } /* else if (parsestat == NSS_STR_PARSE_PARSE) don't care ! */
+ }
+
+ /*
+ * stayopen is set to 0 by default in order to close the opened
+ * file. Some applications may break if it is set to 1.
+ */
+ if (check != 0 && !args->stayopen) {
+ (void) _nss_files_endent(be, 0);
+ }
+
+ return (res);
+}
+
+/*
+ * File hashing support. Critical for sites with large (e.g. 1000+ lines)
+ * /etc/passwd or /etc/group files. Currently only used by getpw*() and
+ * getgr*() routines, but any files backend can use this stuff.
+ */
+static void
+_nss_files_hash_destroy(files_hash_t *fhp)
+{
+ free(fhp->fh_table);
+ fhp->fh_table = NULL;
+ free(fhp->fh_line);
+ fhp->fh_line = NULL;
+ free(fhp->fh_file_start);
+ fhp->fh_file_start = NULL;
+}
+#ifdef PIC
+/*
+ * It turns out the hashing stuff really needs to be disabled for processes
+ * other than the nscd; the consumption of swap space and memory is otherwise
+ * unacceptable when the nscd is killed w/ a large passwd file (4M) active.
+ * See 4031930 for details.
+ * So we just use this psuedo function to enable the hashing feature. Since
+ * this function name is private, we just create a function w/ the name
+ * __nss_use_files_hash in the nscd itself and everyone else uses the old
+ * interface.
+ * We also disable hashing for .a executables to avoid problems with large
+ * files....
+ */
+
+#pragma weak __nss_use_files_hash
+
+extern void __nss_use_files_hash(void);
+#endif /* pic */
+
+nss_status_t
+_nss_files_XY_hash(files_backend_ptr_t be, nss_XbyY_args_t *args,
+ int netdb, files_hash_t *fhp, int hashop, files_XY_check_func check)
+{
+ int fd, retries, ht;
+ uint_t hash, line, f;
+ files_hashent_t *hp, *htab;
+ char *cp, *first, *last;
+ nss_XbyY_args_t xargs;
+ struct stat64 st;
+
+#ifndef PIC
+ return (_nss_files_XY_all(be, args, netdb, 0, check));
+}
+#else
+ if (__nss_use_files_hash == 0)
+ return (_nss_files_XY_all(be, args, netdb, 0, check));
+
+ mutex_lock(&fhp->fh_lock);
+retry:
+ retries = 100;
+ while (stat64(be->filename, &st) < 0) {
+ /*
+ * On a healthy system this can't happen except during brief
+ * periods when the file is being modified/renamed. Keep
+ * trying until things settle down, but eventually give up.
+ */
+ if (--retries == 0)
+ goto unavail;
+ poll(0, 0, 100);
+ }
+
+ if (st.st_mtim.tv_sec == fhp->fh_mtime.tv_sec &&
+ st.st_mtim.tv_nsec == fhp->fh_mtime.tv_nsec &&
+ fhp->fh_table != NULL) {
+ htab = &fhp->fh_table[hashop * fhp->fh_size];
+ hash = fhp->fh_hash_func[hashop](args, 1);
+ for (hp = htab[hash % fhp->fh_size].h_first; hp != NULL;
+ hp = hp->h_next) {
+ if (hp->h_hash != hash)
+ continue;
+ line = hp - htab;
+ if ((*args->str2ent)(fhp->fh_line[line].l_start,
+ fhp->fh_line[line].l_len, args->buf.result,
+ args->buf.buffer, args->buf.buflen) ==
+ NSS_STR_PARSE_SUCCESS) {
+ args->returnval = args->buf.result;
+ if ((*check)(args)) {
+ mutex_unlock(&fhp->fh_lock);
+ return (NSS_SUCCESS);
+ }
+ } else {
+ args->erange = 1;
+ }
+ }
+ args->returnval = 0;
+ mutex_unlock(&fhp->fh_lock);
+ return (NSS_NOTFOUND);
+ }
+
+ _nss_files_hash_destroy(fhp);
+
+ if (st.st_size > SSIZE_MAX)
+ goto unavail;
+
+ if ((fhp->fh_file_start = malloc((ssize_t)st.st_size + 1)) == NULL)
+ goto unavail;
+
+ if ((fd = open(be->filename, O_RDONLY)) < 0)
+ goto unavail;
+
+ if (read(fd, fhp->fh_file_start, (ssize_t)st.st_size) !=
+ (ssize_t)st.st_size) {
+ close(fd);
+ goto retry;
+ }
+
+ close(fd);
+
+ fhp->fh_file_end = fhp->fh_file_start + (off_t)st.st_size;
+ *fhp->fh_file_end = '\n';
+ fhp->fh_mtime = st.st_mtim;
+
+ /*
+ * If the file changed since we read it, or if it's less than
+ * 1-2 seconds old, don't trust it; its modification may still
+ * be in progress. The latter is a heuristic hack to minimize
+ * the likelihood of damage if someone modifies /etc/mumble
+ * directly (as opposed to editing and renaming a temp file).
+ *
+ * Note: the cast to u_int is there in case (1) someone rdated
+ * the system backwards since the last modification of /etc/mumble
+ * or (2) this is a diskless client whose time is badly out of sync
+ * with its server. The 1-2 second age hack doesn't cover these
+ * cases -- oh well.
+ */
+ if (stat64(be->filename, &st) < 0 ||
+ st.st_mtim.tv_sec != fhp->fh_mtime.tv_sec ||
+ st.st_mtim.tv_nsec != fhp->fh_mtime.tv_nsec ||
+ (uint_t)(time(0) - st.st_mtim.tv_sec + 2) < 4) {
+ poll(0, 0, 1000);
+ goto retry;
+ }
+
+ line = 1;
+ for (cp = fhp->fh_file_start; cp < fhp->fh_file_end; cp++)
+ if (*cp == '\n')
+ line++;
+
+ for (f = 2; f * f <= line; f++) { /* find next largest prime */
+ if (line % f == 0) {
+ f = 1;
+ line++;
+ }
+ }
+
+ fhp->fh_size = line;
+ fhp->fh_line = malloc(line * sizeof (files_linetab_t));
+ fhp->fh_table = calloc(line * fhp->fh_nhtab, sizeof (files_hashent_t));
+ if (fhp->fh_line == NULL || fhp->fh_table == NULL)
+ goto unavail;
+
+ xargs = *args;
+ xargs.buf.result = malloc(fhp->fh_resultsize + fhp->fh_bufsize);
+ if (xargs.buf.result == NULL)
+ goto unavail;
+ xargs.buf.buffer = (char *)xargs.buf.result + fhp->fh_resultsize;
+ xargs.buf.buflen = fhp->fh_bufsize;
+ xargs.returnval = xargs.buf.result;
+
+ line = 0;
+ cp = fhp->fh_file_start;
+ while (cp < fhp->fh_file_end) {
+ first = cp;
+ while (*cp != '\n')
+ cp++;
+ if (cp > first && *(cp - 1) == '\\') {
+ memmove(first + 2, first, cp - first - 1);
+ cp = first + 2;
+ continue;
+ }
+ last = cp;
+ *cp++ = '\0';
+ if (netdb) {
+ if ((last = strchr(first, '#')) == 0)
+ last = cp - 1;
+ *last-- = '\0'; /* nuke '\n' or #comment */
+ while (isspace(*first)) /* nuke leading whitespace */
+ first++;
+ if (*first == '\0') /* skip content-free lines */
+ continue;
+ while (isspace(*last)) /* nuke trailing whitespace */
+ --last;
+ *++last = '\0';
+ }
+ if ((*xargs.str2ent)(first, last - first,
+ xargs.buf.result, xargs.buf.buffer, xargs.buf.buflen) !=
+ NSS_STR_PARSE_SUCCESS)
+ continue;
+ for (ht = 0; ht < fhp->fh_nhtab; ht++) {
+ hp = &fhp->fh_table[ht * fhp->fh_size + line];
+ hp->h_hash = fhp->fh_hash_func[ht](&xargs, 0);
+ }
+ fhp->fh_line[line].l_start = first;
+ fhp->fh_line[line++].l_len = last - first;
+ }
+ free(xargs.buf.result);
+
+ /*
+ * Populate the hash tables in reverse order so that the hash chains
+ * end up in forward order. This ensures that hashed lookups find
+ * things in the same order that a linear search of the file would.
+ * This is essential in cases where there could be multiple matches.
+ * For example: until 2.7, root and smtp both had uid 0; but we
+ * certainly wouldn't want getpwuid(0) to return smtp.
+ */
+ for (ht = 0; ht < fhp->fh_nhtab; ht++) {
+ htab = &fhp->fh_table[ht * fhp->fh_size];
+ for (hp = &htab[line - 1]; hp >= htab; hp--) {
+ uint_t bucket = hp->h_hash % fhp->fh_size;
+ hp->h_next = htab[bucket].h_first;
+ htab[bucket].h_first = hp;
+ }
+ }
+
+ goto retry;
+
+unavail:
+ _nss_files_hash_destroy(fhp);
+ mutex_unlock(&fhp->fh_lock);
+ return (NSS_UNAVAIL);
+}
+#endif /* PIC */
+
+nss_status_t
+_nss_files_getent_rigid(be, a)
+ files_backend_ptr_t be;
+ void *a;
+{
+ nss_XbyY_args_t *args = (nss_XbyY_args_t *)a;
+
+ return (_nss_files_XY_all(be, args, 0, 0, 0));
+}
+
+nss_status_t
+_nss_files_getent_netdb(be, a)
+ files_backend_ptr_t be;
+ void *a;
+{
+ nss_XbyY_args_t *args = (nss_XbyY_args_t *)a;
+
+ return (_nss_files_XY_all(be, args, 1, 0, 0));
+}
+
+/*ARGSUSED*/
+nss_status_t
+_nss_files_destr(be, dummy)
+ files_backend_ptr_t be;
+ void *dummy;
+{
+ if (be != 0) {
+ if (be->f != 0) {
+ _nss_files_endent(be, 0);
+ }
+ if (be->hashinfo != NULL) {
+ mutex_lock(&be->hashinfo->fh_lock);
+ if (--be->hashinfo->fh_refcnt == 0)
+ _nss_files_hash_destroy(be->hashinfo);
+ mutex_unlock(&be->hashinfo->fh_lock);
+ }
+ free(be);
+ }
+ return (NSS_SUCCESS); /* In case anyone is dumb enough to check */
+}
+
+nss_backend_t *
+_nss_files_constr(ops, n_ops, filename, min_bufsize, fhp)
+ files_backend_op_t ops[];
+ int n_ops;
+ const char *filename;
+ int min_bufsize;
+ files_hash_t *fhp;
+{
+ files_backend_ptr_t be;
+
+ if ((be = (files_backend_ptr_t)malloc(sizeof (*be))) == 0) {
+ return (0);
+ }
+ be->ops = ops;
+ be->n_ops = n_ops;
+ be->filename = filename;
+ be->minbuf = min_bufsize;
+ be->f = 0;
+ be->buf = 0;
+ be->hashinfo = fhp;
+
+ if (fhp != NULL) {
+ mutex_lock(&fhp->fh_lock);
+ fhp->fh_refcnt++;
+ mutex_unlock(&fhp->fh_lock);
+ }
+
+ return ((nss_backend_t *)be);
+}