# # 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 (c) 1991 - 1996, by Sun Microsystems, Inc. # All rights reserved. # include ../../../Makefile.master FILES_SUBDIRS = $(MACH64) all: TARGET= all clean: TARGET= clean clobber: TARGET= clobber install: TARGET= install .KEEP_STATE: all clean clobber install: $(FILES_SUBDIRS) $(FILES_SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # 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 1993,2001-2003 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # LIBRARY = libnss_compat.a VERS = .1 OBJECTS = getpwent.o \ getgrent.o \ getspent.o \ compat_common.o \ getuserattr.o \ getauuser.o # include common nsswitch library definitions. include ../../Makefile.com # install this library in the root filesystem include ../../../Makefile.rootfs DYNLIB1 = nss_compat.so$(VERS) COMPATLINKS= usr/lib/$(DYNLIB1) # Hammerhead: 64-bit only — COMPATLINKS64 must equal COMPATLINKS # so the amd64/Makefile install rule creates the /usr/lib symlinks. COMPATLINKS64= $(COMPATLINKS) $(ROOT)/usr/lib/$(DYNLIB1) : COMPATLINKTARGET=../../lib/$(DYNLIB1) LDLIBS += -lnsl # # 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 2004 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # include ../Makefile.com include $(SRC)/lib/Makefile.lib.64 LIBS = $(DYNLIB1) include ../../Makefile.targ install: all $(ROOT64DYNLIB) $(ROOTCOMPATLINKS64) /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Common code and structures used by name-service-switch "compat" backends. * * Most of the code in the "compat" backend is a perverted form of code from * the "files" backend; this file is no exception. */ #include #include #include #include #include #include #include #include #include #include /* for GF_PATH */ #include #include "compat_common.h" /* * This should be in a header. */ extern int yp_get_default_domain(char **domain); /* from libc */ extern int str2passwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen); extern int str2spwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen); extern int str2group(const char *instr, int lenstr, void *ent, char *buffer, int buflen); /* from libnsl */ extern char *_strtok_escape(char *, char *, char **); /* * str2auuser_s and str2userattr_s are very simple version * of the str2auuser() and str2userattr() that can be found in * libnsl. They only copy the user name into the userstr_t * or au_user_str_t structure (so check on user name can be * performed). */ static int str2auuser_s( const char *instr, int lenstr, void *ent, char *buffer, int buflen) { char *last = NULL; char *sep = KV_TOKEN_DELIMIT; au_user_str_t *au_user = (au_user_str_t *)ent; if (lenstr >= buflen) return (NSS_STR_PARSE_ERANGE); (void) strncpy(buffer, instr, buflen); au_user->au_name = _strtok_escape(buffer, sep, &last); return (0); } static int str2userattr_s( const char *instr, int lenstr, void *ent, char *buffer, int buflen) { char *last = NULL; char *sep = KV_TOKEN_DELIMIT; userstr_t *user = (userstr_t *)ent; if (lenstr >= buflen) return (NSS_STR_PARSE_ERANGE); (void) strncpy(buffer, instr, buflen); user->name = _strtok_escape(buffer, sep, &last); return (0); } /* * Routines to manage list of "-" users for get{pw, sp, gr}ent(). Current * implementation is completely moronic; we use a linked list. But then * that's what it's always done in 4.x... */ struct setofstrings { char *name; struct setofstrings *next; /* * === Should get smart and malloc the string and pointer as one * object rather than two. */ }; static void strset_free(ssp) strset_t *ssp; { strset_t cur, nxt; for (cur = *ssp; cur != 0; cur = nxt) { nxt = cur->next; free(cur->name); free(cur); } *ssp = 0; } static boolean_t strset_add(ssp, nam) strset_t *ssp; const char *nam; { strset_t new; if (0 == (new = (strset_t)malloc(sizeof (*new)))) { return (B_FALSE); } if (0 == (new->name = malloc(strlen(nam) + 1))) { free(new); return (B_FALSE); } (void) strcpy(new->name, nam); new->next = *ssp; *ssp = new; return (B_TRUE); } static boolean_t strset_in(ssp, nam) const strset_t *ssp; const char *nam; { strset_t cur; for (cur = *ssp; cur != 0; cur = cur->next) { if (strcmp(cur->name, nam) == 0) { return (B_TRUE); } } return (B_FALSE); } /* * Lookup and enumeration routines for +@group and -@group. * * This code knows a lot more about lib/libc/port/gen/getnetgrent.c than * is really healthy. The set/get/end routines below duplicate code * from that file, but keep the state information per-backend-instance * instead of just per-process. */ extern void _nss_initf_netgroup(nss_db_params_t *); /* * Should really share the db_root in getnetgrent.c in order to get the * resource-management quotas right, but this will have to do. */ static DEFINE_NSS_DB_ROOT(netgr_db_root); static boolean_t netgr_in(compat_backend_ptr_t be, const char *group, const char *user) { if (be->yp_domain == 0) { if (yp_get_default_domain((char **)&be->yp_domain) != 0) { return (B_FALSE); } } return (innetgr(group, 0, user, be->yp_domain)); } static void netgr_set(be, netgroup) compat_backend_ptr_t be; const char *netgroup; { /* * ===> Need comment to explain that this first "if" is optimizing * for the same-netgroup-as-last-time case */ if (be->getnetgrent_backend != 0 && NSS_INVOKE_DBOP(be->getnetgrent_backend, NSS_DBOP_SETENT, (void *) netgroup) != NSS_SUCCESS) { NSS_INVOKE_DBOP(be->getnetgrent_backend, NSS_DBOP_DESTRUCTOR, 0); be->getnetgrent_backend = 0; } if (be->getnetgrent_backend == 0) { struct nss_setnetgrent_args args; args.netgroup = netgroup; args.iterator = 0; (void) nss_search(&netgr_db_root, _nss_initf_netgroup, NSS_DBOP_NETGROUP_SET, &args); be->getnetgrent_backend = args.iterator; } } static boolean_t netgr_next_u(be, up) compat_backend_ptr_t be; char **up; { if (be->netgr_buffer == 0 && (be->netgr_buffer = malloc(NSS_BUFLEN_NETGROUP)) == 0) { /* Out of memory */ return (B_FALSE); } do { struct nss_getnetgrent_args args; args.buffer = be->netgr_buffer; args.buflen = NSS_BUFLEN_NETGROUP; args.status = NSS_NETGR_NO; if (be->getnetgrent_backend != 0) { NSS_INVOKE_DBOP(be->getnetgrent_backend, NSS_DBOP_GETENT, &args); } if (args.status == NSS_NETGR_FOUND) { *up = args.retp[NSS_NETGR_USER]; } else { return (B_FALSE); } } while (*up == 0); return (B_TRUE); } static void netgr_end(be) compat_backend_ptr_t be; { if (be->getnetgrent_backend != 0) { NSS_INVOKE_DBOP(be->getnetgrent_backend, NSS_DBOP_DESTRUCTOR, 0); be->getnetgrent_backend = 0; } if (be->netgr_buffer != 0) { free(be->netgr_buffer); be->netgr_buffer = 0; } } #define MAXFIELDS 9 /* Sufficient for passwd (7), shadow (9), group (4) */ static nss_status_t do_merge(be, args, instr, linelen) compat_backend_ptr_t be; nss_XbyY_args_t *args; const char *instr; int linelen; { char *fields[MAXFIELDS]; int i; int overrides; const char *p; const char *end = instr + linelen; nss_status_t res = NSS_NOTFOUND; /* * Potential optimization: only perform the field-splitting nonsense * once per input line (at present, "+" and "+@netgroup" entries * will cause us to do this multiple times in getent() requests). */ for (i = 0; i < MAXFIELDS; i++) { fields[i] = 0; } for (p = instr, overrides = 0, i = 0; /* no test */; i++) { const char *q = memchr(p, ':', end - p); const char *r = (q == 0) ? end : q; ssize_t len = r - p; if (len > 0) { char *s = malloc(len + 1); if (s == 0) { overrides = -1; /* Indicates "you lose" */ break; } (void) memcpy(s, p, len); s[len] = '\0'; fields[i] = s; overrides++; } if (q == 0) { /* End of line */ break; } else { /* Skip the colon at (*q) */ p = q + 1; } } if (overrides == 1) { /* * return result here if /etc file format is requested */ if (be->return_string_data != 1) { /* No real overrides, return (*args) intact */ res = NSS_SUCCESS; } else { free(fields[0]); fields[0] = NULL; } } if (overrides > 1 || be->return_string_data == 1) { /* * The zero'th field is always nonempty (+/-...), but at least * one other field was also nonempty, i.e. wants to override */ switch ((*be->mergef)(be, args, (const char **)fields)) { case NSS_STR_PARSE_SUCCESS: if (be->return_string_data != 1) args->returnval = args->buf.result; else args->returnval = args->buf.buffer; args->erange = 0; res = NSS_SUCCESS; break; case NSS_STR_PARSE_ERANGE: args->returnval = 0; args->erange = 1; res = NSS_NOTFOUND; break; case NSS_STR_PARSE_PARSE: args->returnval = 0; args->erange = 0; /* ===> Very likely the wrong thing to do... */ res = NSS_NOTFOUND; break; } } else if (res != NSS_SUCCESS) { args->returnval = 0; args->erange = 0; res = NSS_UNAVAIL; /* ==> Right? */ } for (i = 0; i < MAXFIELDS; i++) { if (fields[i] != 0) { free(fields[i]); } } return (res); } /*ARGSUSED*/ nss_status_t _nss_compat_setent(be, dummy) compat_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 = fopen(be->filename, "rF")) == 0) { return (NSS_UNAVAIL); } } else { rewind(be->f); } strset_free(&be->minuses); /* ===> ??? nss_endent(be->db_rootp, be->db_initf, &be->db_context); */ if ((strcmp(be->filename, USERATTR_FILENAME) == 0) || (strcmp(be->filename, AUDITUSER_FILENAME) == 0)) be->state = GETENT_ATTRDB; else be->state = GETENT_FILE; be->return_string_data = 0; /* ===> ?? netgroup stuff? */ return (NSS_SUCCESS); } /*ARGSUSED*/ nss_status_t _nss_compat_endent(be, dummy) compat_backend_ptr_t be; void *dummy; { if (be->f != 0) { (void) fclose(be->f); be->f = 0; } if (be->buf != 0) { free(be->buf); be->buf = 0; } nss_endent(be->db_rootp, be->db_initf, &be->db_context); be->state = GETENT_FILE; /* Probably superfluous but comforting */ strset_free(&be->minuses); netgr_end(be); /* * Question: from the point of view of resource-freeing vs. time to * start up again, how much should we do in endent() and how much * in the destructor? */ return (NSS_SUCCESS); } /*ARGSUSED*/ nss_status_t _nss_compat_destr(be, dummy) compat_backend_ptr_t be; void *dummy; { if (be != 0) { if (be->f != 0) { (void) _nss_compat_endent(be, 0); } nss_delete(be->db_rootp); nss_delete(&netgr_db_root); free(be->workarea); free(be); } return (NSS_SUCCESS); /* In case anyone is dumb enough to check */ } static int read_line(f, buffer, buflen) FILE *f; char *buffer; int buflen; { /*CONSTCOND*/ while (1) { int linelen; if (fgets(buffer, buflen, f) == 0) { /* End of file */ return (-1); } linelen = strlen(buffer); /* linelen >= 1 (since fgets didn't return 0) */ if (buffer[linelen - 1] == '\n') { /* * ===> The code below that calls read_line() doesn't * play by the rules; it assumes in places that * the line is null-terminated. For now we'll * humour it. */ buffer[--linelen] = '\0'; return (linelen); } if (feof(f)) { /* Line is last line in file, and has no newline */ return (linelen); } /* Line too long for buffer; toss it and loop for next line */ /* ===== should syslog() in cases where previous code did */ while (fgets(buffer, buflen, f) != 0 && buffer[strlen(buffer) - 1] != '\n') { ; } } /*NOTREACHED*/ } static int is_nss_lookup_by_name(int attrdb, nss_dbop_t op) { int result = 0; if ((attrdb != 0) && ((op == NSS_DBOP_AUDITUSER_BYNAME) || (op == NSS_DBOP_USERATTR_BYNAME))) { result = 1; } else if ((attrdb == 0) && ((op == NSS_DBOP_GROUP_BYNAME) || (op == NSS_DBOP_PASSWD_BYNAME) || (op == NSS_DBOP_SHADOW_BYNAME))) { result = 1; } return (result); } /*ARGSUSED*/ nss_status_t _attrdb_compat_XY_all(be, argp, netdb, check, op_num) compat_backend_ptr_t be; nss_XbyY_args_t *argp; int netdb; compat_XY_check_func check; nss_dbop_t op_num; { int parsestat; int (*func)(); const char *filter = argp->key.name; nss_status_t res; #ifdef DEBUG (void) fprintf(stdout, "\n[compat_common.c: _attrdb_compat_XY_all]\n"); #endif /* DEBUG */ if (be->buf == 0 && (be->buf = malloc(be->minbuf)) == 0) { return (NSS_UNAVAIL); } if (check != NULL) if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) return (res); res = NSS_NOTFOUND; /* * assume a NULL buf.result pointer is an indication * that the lookup result should be returned in /etc * file format (if called from _nss_compat_getent(), * be->return_string_data and argp->buf.result * would be set already if argp->buf.result is NULL) */ if (check != NULL) { if (argp->buf.result == NULL) { be->return_string_data = 1; /* * the code executed later needs the result struct * as working area */ argp->buf.result = be->workarea; } else be->return_string_data = 0; } /* * use an alternate str2ent function if necessary */ if (be->return_string_data == 1) func = be->str2ent_alt; else func = argp->str2ent; /*CONSTCOND*/ while (1) { int linelen; char *instr = be->buf; if ((linelen = read_line(be->f, instr, be->minbuf)) < 0) { /* End of file */ argp->returnval = 0; argp->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; } } argp->returnval = 0; parsestat = (*func)(instr, linelen, argp->buf.result, argp->buf.buffer, argp->buf.buflen); if (parsestat == NSS_STR_PARSE_SUCCESS) { argp->returnval = argp->buf.result; if (check == 0 || (*check)(argp)) { int len; if (be->return_string_data != 1) { res = NSS_SUCCESS; break; } /* copy string data to result buffer */ argp->buf.result = NULL; argp->returnval = argp->buf.buffer; if ((len = strlcpy(argp->buf.buffer, instr, argp->buf.buflen)) >= argp->buf.buflen) { argp->returnval = NULL; res = NSS_NOTFOUND; argp->erange = 1; break; } argp->returnlen = len; res = NSS_SUCCESS; break; } } else if (parsestat == NSS_STR_PARSE_ERANGE) { res = NSS_NOTFOUND; argp->erange = 1; break; } } /* * 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 && !argp->stayopen) { (void) _nss_compat_endent(be, 0); } if (res != NSS_SUCCESS) { /* * tell the nss_search() and nss_getent() below * if the result should be returned in the /etc * file format */ if (be->return_string_data == 1) argp->buf.result = NULL; if ((op_num == NSS_DBOP_USERATTR_BYNAME) || (op_num == NSS_DBOP_AUDITUSER_BYNAME)) { res = nss_search(be->db_rootp, be->db_initf, op_num, argp); } else { res = nss_getent(be->db_rootp, be->db_initf, &be->db_context, argp); } if (res != NSS_SUCCESS) { argp->returnval = 0; argp->erange = 0; } } return (res); } static int validate_ids(compat_backend_ptr_t be, nss_XbyY_args_t *argp, char *line, int *linelenp, int buflen, int extra_chars) { if (be->return_string_data != 1) { struct passwd *p; struct group *g; /* * The data is already marshalled into * struct passwd or group. */ if (strcmp(be->filename, PASSWD) == 0) { p = (struct passwd *)argp->returnval; if (p->pw_uid > MAXUID) p->pw_uid = UID_NOBODY; if (p->pw_gid > MAXUID) p->pw_gid = GID_NOBODY; } else if (strcmp(be->filename, GF_PATH) == 0) { g = (struct group *)argp->returnval; if (g->gr_gid > MAXUID) g->gr_gid = GID_NOBODY; } return (NSS_STR_PARSE_SUCCESS); } /* * The data needs to be returned in string format therefore * validate the return string. */ if (strcmp(be->filename, PASSWD) == 0) return (validate_passwd_ids(line, linelenp, buflen, extra_chars)); else if (strcmp(be->filename, GF_PATH) == 0) return (validate_group_ids(line, linelenp, buflen, extra_chars)); return (NSS_STR_PARSE_SUCCESS); } nss_status_t _nss_compat_XY_all(be, args, check, op_num) compat_backend_ptr_t be; nss_XbyY_args_t *args; compat_XY_check_func check; nss_dbop_t op_num; { nss_status_t res; int parsestat; if (be->buf == 0 && (be->buf = malloc(be->minbuf)) == 0) { return (NSS_UNAVAIL); /* really panic, malloc failed */ } if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) { return (res); } res = NSS_NOTFOUND; /* * assume a NULL buf.result pointer is an indication * that the lookup result should be returned in /etc * file format */ if (args->buf.result == NULL) { be->return_string_data = 1; /* * the code executed later needs the result struct * as working area */ args->buf.result = be->workarea; be->str2ent_save = args->str2ent; args->str2ent = be->str2ent_alt; } else be->return_string_data = 0; /*CONSTCOND*/ while (1) { int linelen; char *instr = be->buf; char *colon; linelen = read_line(be->f, instr, be->minbuf); if (linelen < 0) { /* End of file */ args->returnval = 0; args->erange = 0; break; } args->returnval = 0; /* reset for both types of entries */ if (instr[0] != '+' && instr[0] != '-') { /* Simple, wholesome, God-fearing entry */ parsestat = (*args->str2ent)(instr, linelen, args->buf.result, args->buf.buffer, args->buf.buflen); if (parsestat == NSS_STR_PARSE_SUCCESS) { args->returnval = args->buf.result; if ((*check)(args) != 0) { int len; parsestat = validate_ids(be, args, instr, &linelen, be->minbuf, 1); if (parsestat == NSS_STR_PARSE_ERANGE) { args->erange = 1; res = NSS_NOTFOUND; break; } else if (parsestat != NSS_STR_PARSE_SUCCESS) { continue; } if (be->return_string_data != 1) { res = NSS_SUCCESS; break; } /* * copy string data to * result buffer */ args->buf.result = NULL; args->str2ent = be->str2ent_save; if ((len = strlcpy(args->buf.buffer, instr, args->buf.buflen)) >= args->buf.buflen) parsestat = NSS_STR_PARSE_ERANGE; else { args->returnval = args->buf.buffer; args->returnlen = len; res = NSS_SUCCESS; break; } } else continue; } /* ===> Check the Dani logic here... */ if (parsestat == NSS_STR_PARSE_ERANGE) { args->erange = 1; res = NSS_NOTFOUND; break; /* should we just skip this one long line ? */ } /* else if (parsestat == NSS_STR_PARSE_PARSE) */ /* don't care ! */ /* ==> ?? */ continue; } /* * Process "+", "+name", "+@netgroup", "-name" or "-@netgroup" * * This code is optimized for lookups by name. * * For lookups by identifier search key cannot be matched with * the name of the "+" or "-" entry. So nss_search() is to be * called before extracting the name i.e. via (*be->getnamef)(). * * But for lookups by name, search key is compared with the name * of the "+" or "-" entry to acquire a match and thus * unnesessary calls to nss_search() is eliminated. Also for * matching "-" entries, calls to nss_search() is eliminated. */ if ((colon = strchr(instr, ':')) != 0) { *colon = '\0'; /* terminate field to extract name */ } if (instr[1] == '@') { /* * Case 1: * The entry is of the form "+@netgroup" or * "-@netgroup". If we're performing a lookup by name, * we can simply extract the name from the search key * (i.e. args->key.name). If not, then we must call * nss_search() before extracting the name via the * get_XXname() function. i.e. (*be->getnamef)(args). */ if (is_nss_lookup_by_name(0, op_num) != 0) { /* compare then search */ if (!be->permit_netgroups || !netgr_in(be, instr + 2, args->key.name)) continue; if (instr[0] == '+') { /* need to search for "+" entry */ (void) nss_search(be->db_rootp, be->db_initf, op_num, args); if (args->returnval == 0) continue; } } else { /* search then compare */ (void) nss_search(be->db_rootp, be->db_initf, op_num, args); if (args->returnval == 0) continue; if (!be->permit_netgroups || !netgr_in(be, instr + 2, (*be->getnamef)(args))) continue; } } else if (instr[1] == '\0') { /* * Case 2: * The entry is of the form "+" or "-". The former * allows all entries from name services. The latter * is illegal and ought to be ignored. */ if (instr[0] == '-') continue; /* need to search for "+" entry */ (void) nss_search(be->db_rootp, be->db_initf, op_num, args); if (args->returnval == 0) continue; } else { /* * Case 3: * The entry is of the form "+name" or "-name". * If we're performing a lookup by name, we can simply * extract the name from the search key * (i.e. args->key.name). If not, then we must call * nss_search() before extracting the name via the * get_XXname() function. i.e. (*be->getnamef)(args). */ if (is_nss_lookup_by_name(0, op_num) != 0) { /* compare then search */ if (strcmp(instr + 1, args->key.name) != 0) continue; if (instr[0] == '+') { /* need to search for "+" entry */ (void) nss_search(be->db_rootp, be->db_initf, op_num, args); if (args->returnval == 0) continue; } } else { /* search then compare */ (void) nss_search(be->db_rootp, be->db_initf, op_num, args); if (args->returnval == 0) continue; if (strcmp(instr + 1, (*be->getnamef)(args)) != 0) continue; } } if (instr[0] == '-') { /* no need to search for "-" entry */ args->returnval = 0; args->erange = 0; res = NSS_NOTFOUND; } else { if (colon != 0) *colon = ':'; /* restoration */ res = do_merge(be, args, instr, linelen); } break; } /* * 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 (!args->stayopen) { (void) _nss_compat_endent(be, 0); } if (be->return_string_data == 1) { args->str2ent = be->str2ent_save; } return (res); } nss_status_t _nss_compat_getent(be, a) compat_backend_ptr_t be; void *a; { nss_XbyY_args_t *args = (nss_XbyY_args_t *)a; nss_status_t res; char *colon = 0; /* <=== need comment re lifetime */ if (be->f == 0) { if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) { return (res); } } if (be->buf == 0 && (be->buf = malloc(be->minbuf)) == 0) { return (NSS_UNAVAIL); /* really panic, malloc failed */ } /* * assume a NULL buf.result pointer is an indication * that the lookup result should be returned in /etc * file format */ if (args->buf.result == NULL) { be->return_string_data = 1; /* * the code executed later needs the result struct * as working area */ args->buf.result = be->workarea; } else be->return_string_data = 0; /*CONSTCOND*/ while (1) { char *instr = be->buf; int linelen; char *name; /* === Need more distinctive label */ const char *savename; /* * In the code below... * break means "I found one, I think" (i.e. goto the * code after the end of the switch statement), * continue means "Next candidate" * (i.e. loop around to the switch statement), * return means "I'm quite sure" (either Yes or No). */ switch (be->state) { case GETENT_DONE: args->returnval = 0; args->erange = 0; return (NSS_NOTFOUND); case GETENT_ATTRDB: args->key.name = NULL; res = _attrdb_compat_XY_all(be, args, 1, (compat_XY_check_func)NULL, 0); return (res); case GETENT_FILE: linelen = read_line(be->f, instr, be->minbuf); if (linelen < 0) { /* End of file */ be->state = GETENT_DONE; continue; } if ((colon = strchr(instr, ':')) != 0) { *colon = '\0'; } if (instr[0] == '-') { if (instr[1] != '@') { (void) strset_add(&be->minuses, instr + 1); } else if (be->permit_netgroups) { netgr_set(be, instr + 2); while (netgr_next_u(be, &name)) { (void) strset_add(&be->minuses, name); } netgr_end(be); } /* Else (silently) ignore the entry */ continue; } else if (instr[0] != '+') { int parsestat; /* * Normal entry, no +/- nonsense */ if (colon != 0) { *colon = ':'; } args->returnval = 0; parsestat = (*args->str2ent)(instr, linelen, args->buf.result, args->buf.buffer, args->buf.buflen); if (parsestat == NSS_STR_PARSE_SUCCESS) { int len; if (be->return_string_data != 1) { args->returnval = args->buf.result; return (NSS_SUCCESS); } /* * copy string data to * result buffer */ args->buf.result = NULL; args->returnval = args->buf.buffer; if ((len = strlcpy(args->buf.buffer, instr, args->buf.buflen)) >= args->buf.buflen) parsestat = NSS_STR_PARSE_ERANGE; else { args->returnlen = len; return (NSS_SUCCESS); } } /* ==> ?? Treat ERANGE differently ?? */ if (parsestat == NSS_STR_PARSE_ERANGE) { args->returnval = 0; args->erange = 1; return (NSS_NOTFOUND); } /* Skip the offending entry, get next */ continue; } else if (instr[1] == '\0') { /* Plain "+" */ nss_setent(be->db_rootp, be->db_initf, &be->db_context); be->state = GETENT_ALL; be->linelen = linelen; continue; } else if (instr[1] == '@') { /* "+@netgroup" */ netgr_set(be, instr + 2); be->state = GETENT_NETGROUP; be->linelen = linelen; continue; } else { /* "+name" */ name = instr + 1; break; } /* NOTREACHED */ case GETENT_ALL: linelen = be->linelen; args->returnval = 0; if (be->return_string_data == 1) { be->str2ent_save = args->str2ent; args->str2ent = be->str2ent_alt; } (void) nss_getent(be->db_rootp, be->db_initf, &be->db_context, args); if (args->returnval == 0) { /* ==> ?? Treat ERANGE differently ?? */ nss_endent(be->db_rootp, be->db_initf, &be->db_context); be->state = GETENT_FILE; if (be->return_string_data == 1) args->str2ent = be->str2ent_save; continue; } if (strset_in(&be->minuses, (*be->getnamef)(args))) continue; name = 0; /* tell code below we've done the lookup */ if (be->return_string_data == 1) args->str2ent = be->str2ent_save; break; case GETENT_NETGROUP: linelen = be->linelen; if (!netgr_next_u(be, &name)) { netgr_end(be); be->state = GETENT_FILE; continue; } /* pass "name" variable to code below... */ break; } if (name != 0) { if (strset_in(&be->minuses, name)) { continue; } /* * Do a getXXXnam(name). If we were being pure, * we'd introduce yet another function-pointer * that the database-specific code had to supply * to us. Instead we'll be grotty and hard-code * the knowledge that * (a) The username is always passwd in key.name, * (b) NSS_DBOP_PASSWD_BYNAME == * NSS_DBOP_SHADOW_BYNAME == * NSS_DBOP_next_iter. */ savename = args->key.name; args->key.name = name; args->returnval = 0; if (be->return_string_data == 1) { be->str2ent_save = args->str2ent; args->str2ent = be->str2ent_alt; } (void) nss_search(be->db_rootp, be->db_initf, NSS_DBOP_next_iter, args); if (be->return_string_data == 1) args->str2ent = be->str2ent_save; args->key.name = savename; /* In case anyone cares */ } /* * Found one via "+", "+name" or "@netgroup". * Override some fields if the /etc file says to do so. */ if (args->returnval == 0) { /* ==> ?? Should treat erange differently? */ continue; } /* 'colon' was set umpteen iterations ago in GETENT_FILE */ if (colon != 0) { *colon = ':'; colon = 0; } return (do_merge(be, args, instr, linelen)); } /*NOTREACHED*/ } /* We don't use this directly; we just copy the bits when we want to */ /* initialize the variable (in the compat_backend struct) that we do use */ static DEFINE_NSS_GETENT(context_initval); nss_backend_t * _nss_compat_constr(ops, n_ops, filename, min_bufsize, rootp, initf, netgroups, getname_func, merge_func) compat_backend_op_t ops[]; int n_ops; const char *filename; int min_bufsize; nss_db_root_t *rootp; nss_db_initf_t initf; int netgroups; compat_get_name getname_func; compat_merge_func merge_func; { compat_backend_ptr_t be; if ((be = (compat_backend_ptr_t)malloc(sizeof (*be))) == 0) { return (0); } be->ops = ops; be->n_ops = n_ops; be->filename = filename; be->f = 0; be->minbuf = min_bufsize; be->buf = 0; be->db_rootp = rootp; be->db_initf = initf; be->db_context = context_initval; be->getnamef = getname_func; be->mergef = merge_func; be->state = GETENT_FILE; /* i.e. do Automatic setent(); */ if (strcmp(be->filename, USERATTR_FILENAME) == 0) { be->state = GETENT_ATTRDB; be->str2ent_alt = str2userattr_s; be->workarea = calloc(1, sizeof (userstr_t)); } else if (strcmp(be->filename, AUDITUSER_FILENAME) == 0) { be->state = GETENT_ATTRDB; be->str2ent_alt = str2auuser_s; be->workarea = calloc(1, sizeof (au_user_str_t)); } else if (strcmp(be->filename, PASSWD) == 0) { be->str2ent_alt = str2passwd; be->workarea = calloc(1, sizeof (struct passwd)); } else if (strcmp(be->filename, SHADOW) == 0) { be->str2ent_alt = str2spwd; be->workarea = calloc(1, sizeof (struct spwd)); } else { /* group */ be->str2ent_alt = str2group; be->workarea = calloc(1, sizeof (struct group)); } if (be->workarea == NULL) return (NULL); be->minuses = 0; be->permit_netgroups = netgroups; be->yp_domain = 0; be->getnetgrent_backend = 0; be->netgr_buffer = 0; be->return_string_data = 0; return ((nss_backend_t *)be); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Common code and structures used by name-service-switch "compat" backends. */ #ifndef _COMPAT_COMMON_H #define _COMPAT_COMMON_H #include #include #include #ifdef __cplusplus extern "C" { #endif typedef struct compat_backend *compat_backend_ptr_t; typedef nss_status_t (*compat_backend_op_t)(compat_backend_ptr_t, void *); /* * ===> Fix da comments (and in files_common.h too...) * Iterator function for _nss_files_do_all(), which probably calls yp_all(). * NSS_NOTFOUND means "keep enumerating", NSS_SUCCESS means"return now", * other values don't make much sense. In other words we're abusing * (overloading) the meaning of nss_status_t, but hey... * _nss_compat_XY_all() is a wrapper around _nss_files_do_all() that does the * generic work for nss_XbyY_args_t backends (calls cstr2ent etc). */ typedef nss_status_t (*files_do_all_func_t)(const char *, int, void *args); /* ===> ^^ nuke this line */ typedef int (*compat_XY_check_func)(nss_XbyY_args_t *); typedef const char *(*compat_get_name)(nss_XbyY_args_t *); typedef int (*compat_merge_func)(compat_backend_ptr_t, nss_XbyY_args_t *, const char **fields); typedef struct setofstrings *strset_t; struct compat_backend { compat_backend_op_t *ops; int n_ops; const char *filename; FILE *f; int minbuf; char *buf; int linelen; /* <== Explain use, lifetime */ nss_db_initf_t db_initf; nss_db_root_t *db_rootp; /* Shared between instances */ nss_getent_t db_context; /* Per-instance enumeration */ compat_get_name getnamef; compat_merge_func mergef; /* We wouldn't need all this hokey state stuff if we */ /* used another thread to implement a coroutine... */ enum { GETENT_FILE, GETENT_NETGROUP, GETENT_ATTRDB, GETENT_ALL, GETENT_DONE } state; strset_t minuses; int permit_netgroups; const char *yp_domain; nss_backend_t *getnetgrent_backend; char *netgr_buffer; int return_string_data; int (*str2ent_save)(); int (*str2ent_alt)(); void *workarea; }; #if defined(__STDC__) extern nss_backend_t *_nss_compat_constr(compat_backend_op_t *ops, int n_ops, const char *filename, int min_bufsize, nss_db_root_t *rootp, nss_db_initf_t initf, int netgroups, compat_get_name getname_func, compat_merge_func merge_func); extern nss_status_t _nss_compat_destr(compat_backend_ptr_t, void *dummy); extern nss_status_t _nss_compat_setent(compat_backend_ptr_t, void *dummy); extern nss_status_t _nss_compat_endent(compat_backend_ptr_t, void *dummy); extern nss_status_t _nss_compat_getent(compat_backend_ptr_t, void *); extern nss_status_t _nss_compat_XY_all(compat_backend_ptr_t, nss_XbyY_args_t *args, compat_XY_check_func check, nss_dbop_t op_num); extern nss_status_t _attrdb_compat_XY_all(compat_backend_ptr_t, nss_XbyY_args_t *args, int netdb, compat_XY_check_func check, nss_dbop_t op_num); #else extern nss_backend_t *_nss_compat_constr(); extern nss_status_t _nss_compat_destr(); extern nss_status_t _nss_compat_setent(); extern nss_status_t _nss_compat_endent(); extern nss_status_t _nss_compat_getent(); extern nss_status_t _nss_compat_XY_all(); extern nss_status_t _attrdb_compat_XY_all(); #endif /* functions to validate passwd and group ids */ extern int validate_passwd_ids(char *line, int *linelenp, int buflen, int extra_chars); extern int validate_group_ids(char *line, int *linelenp, int buflen, int extra_chars); #ifdef __cplusplus } #endif #endif /* _COMPAT_COMMON_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include "compat_common.h" static DEFINE_NSS_DB_ROOT(db_root); static void _nss_initf_auuser_compat(nss_db_params_t *p) { p->name = NSS_DBNAM_AUDITUSER; p->config_name = NSS_DBNAM_PASSWD_COMPAT; p->default_config = NSS_DEFCONF_PASSWD_COMPAT; } static const char * get_auname(nss_XbyY_args_t *argp) { au_user_str_t *au_user = (au_user_str_t *)argp->returnval; return (au_user->au_name); } static int check_name(nss_XbyY_args_t *argp) { au_user_str_t *au_user = (au_user_str_t *)argp->returnval; const char *name = argp->key.name; #ifdef DEBUG (void) fprintf(stdout, "\n[getauuser.c: check_name %s with %s]\n", au_user->au_name, name); #endif /* DEBUG */ if (strcmp(au_user->au_name, name) == 0) { return (1); } return (0); } static nss_status_t getbynam(compat_backend_ptr_t be, void *a) { nss_status_t res; nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; #ifdef DEBUG (void) fprintf(stdout, "\n[getauuser.c: getbynam]\n"); #endif /* DEBUG */ res = _attrdb_compat_XY_all(be, argp, 1, check_name, NSS_DBOP_AUDITUSER_BYNAME); return (res); } static compat_backend_op_t auuser_ops[] = { _nss_compat_destr, _nss_compat_endent, _nss_compat_setent, _nss_compat_getent, getbynam }; /*ARGSUSED*/ nss_backend_t * _nss_compat_audit_user_constr(const char *dummy1, const char *dummy2, const char *dummy3, const char *dummy4, const char *dummy5) { return (_nss_compat_constr(auuser_ops, sizeof (auuser_ops)/sizeof (auuser_ops[0]), AUDITUSER_FILENAME, NSS_LINELEN_AUDITUSER, &db_root, _nss_initf_auuser_compat, 0, get_auname, NULL)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 */ /* * getgrent.c * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * lib/nsswitch/compat/getgrent.c -- name-service-switch backend for getgrnam() * et al that does 4.x compatibility. It looks in /etc/group; if it finds * group entries there that begin with "+" or "-", it consults other * services. By default it uses NIS (YP), but the user can override this * with a "group_compat" entry in /etc/nsswitch.conf, e.g. * group_compat: ldap * * This code tries to produce the same results as the 4.x code, even when * the latter seems ill thought-out. Bug-compatible, in other words. * Though we do try to be more reasonable about the format of "+" and "-" * entries here, i.e. you don't have to pad them with spurious colons and * bogus uid/gid values. * * Caveats: * - More than one source may be specified, with the usual switch semantics, * but having multiple sources here is definitely odd. * - People who recursively specify "compat" deserve what they get. */ #include #include #include /* for GF_PATH */ #include #include "compat_common.h" static DEFINE_NSS_DB_ROOT(db_root); static void _nss_initf_group_compat(p) nss_db_params_t *p; { p->name = NSS_DBNAM_GROUP; p->config_name = NSS_DBNAM_GROUP_COMPAT; p->default_config = NSS_DEFCONF_GROUP_COMPAT; } /* * Validates group entry replacing gid > MAXUID by GID_NOBODY. */ int validate_group_ids(char *line, int *linelenp, int buflen, int extra_chars) { char *linep, *limit, *gidp; ulong_t gid; int oldgidlen, idlen; int linelen = *linelenp, newlinelen; if (linelen == 0 || *line == '+' || *line == '-') return (NSS_STR_PARSE_SUCCESS); linep = line; limit = line + linelen; while (linep < limit && *linep++ != ':') /* skip groupname */ continue; while (linep < limit && *linep++ != ':') /* skip password */ continue; if (linep == limit) return (NSS_STR_PARSE_PARSE); gidp = linep; gid = strtoul(gidp, (char **)&linep, 10); /* grab gid */ oldgidlen = linep - gidp; if (linep >= limit || oldgidlen == 0) return (NSS_STR_PARSE_PARSE); if (gid <= MAXUID) return (NSS_STR_PARSE_SUCCESS); idlen = snprintf(NULL, 0, "%u", GID_NOBODY); newlinelen = linelen + idlen - oldgidlen; if (newlinelen + extra_chars > buflen) return (NSS_STR_PARSE_ERANGE); (void) bcopy(linep, gidp + idlen, limit - linep + extra_chars); (void) snprintf(gidp, idlen + 1, "%u", GID_NOBODY); *(gidp + idlen) = ':'; *linelenp = newlinelen; return (NSS_STR_PARSE_SUCCESS); } static const char * get_grname(argp) nss_XbyY_args_t *argp; { struct group *g = (struct group *)argp->returnval; return (g->gr_name); } static int check_grname(argp) nss_XbyY_args_t *argp; { struct group *g = (struct group *)argp->returnval; return (strcmp(g->gr_name, argp->key.name) == 0); } static nss_status_t getbyname(be, a) compat_backend_ptr_t be; void *a; { nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; return (_nss_compat_XY_all(be, argp, check_grname, NSS_DBOP_GROUP_BYNAME)); } static int check_grgid(argp) nss_XbyY_args_t *argp; { struct group *g = (struct group *)argp->returnval; return (g->gr_gid == argp->key.gid); } static nss_status_t getbygid(be, a) compat_backend_ptr_t be; void *a; { nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; if (argp->key.gid > MAXUID) return (NSS_NOTFOUND); return (_nss_compat_XY_all(be, argp, check_grgid, NSS_DBOP_GROUP_BYGID)); } static nss_status_t getbymember(be, a) compat_backend_ptr_t be; void *a; { struct nss_groupsbymem *argp = (struct nss_groupsbymem *)a; int numgids = argp->numgids; int maxgids = argp->maxgids; gid_t *gid_array = argp->gid_array; struct nss_XbyY_args grargs; struct group *g; nss_XbyY_buf_t *gb = NULL, *b = NULL; /* * Generic implementation: enumerate using getent(), then check each * group returned by getent() to see whether it contains the user. * There are much faster ways, but at least this one gets the right * answer. */ if (numgids >= maxgids) { /* full gid_array; nobody should have bothered to call us */ return (NSS_SUCCESS); } b = NSS_XbyY_ALLOC(&gb, sizeof (struct group), NSS_BUFLEN_GROUP); if (b == 0) return (NSS_UNAVAIL); NSS_XbyY_INIT(&grargs, gb->result, gb->buffer, gb->buflen, argp->str2ent); g = (struct group *)gb->result; (void) _nss_compat_setent(be, 0); while (_nss_compat_getent(be, &grargs) == NSS_SUCCESS) { char **mem; if (grargs.returnval == 0) { continue; } for (mem = g->gr_mem; *mem != 0; mem++) { if (strcmp(*mem, argp->username) == 0) { int gid = g->gr_gid; int i; for (i = 0; i < numgids; i++) { if (gid == gid_array[i]) { break; } } if (i == numgids) { gid_array[numgids++] = gid; argp->numgids = numgids; if (numgids >= maxgids) { /* filled the gid_array */ (void) _nss_compat_endent(be, 0); NSS_XbyY_FREE(&gb); return (NSS_SUCCESS); } /* Done with this group, try next */ break; } } } } (void) _nss_compat_endent(be, 0); NSS_XbyY_FREE(&gb); return (NSS_NOTFOUND); /* Really means "gid_array not full yet" */ } /*ARGSUSED*/ static int merge_grents(be, argp, fields) compat_backend_ptr_t be; nss_XbyY_args_t *argp; const char **fields; { struct group *g = (struct group *)argp->buf.result; char *buf; char *s; int parsestat; int dlen; /* * We're allowed to override the passwd (has anyone ever actually used * the passwd in a group entry?) and the membership list, but not * the groupname or the gid. * That's what the SunOS 4.x code did; who are we to question it... * * Efficiency is heartlessly abandoned in the quest for simplicity. */ if (fields[1] == 0 && fields[3] == 0 && be->return_string_data != 1) { /* No legal overrides, leave *argp unscathed */ return (NSS_STR_PARSE_SUCCESS); } if ((buf = malloc(NSS_LINELEN_GROUP)) == 0) { return (NSS_STR_PARSE_PARSE); /* Really "out of memory", but PARSE_PARSE will have to do */ } s = buf; (void) snprintf(s, NSS_LINELEN_GROUP, "%s:%s:%u:", g->gr_name, fields[1] != 0 ? fields[1] : g->gr_passwd, g->gr_gid); s += strlen(s); if (fields[3] != 0) { (void) strcpy(s, fields[3]); s += strlen(s); } else { char **memp; for (memp = g->gr_mem; *memp != 0; memp++) { size_t len = strlen(*memp); if (s + len + 1 <= buf + NSS_LINELEN_GROUP) { if (memp != g->gr_mem) { *s++ = ','; } (void) memcpy(s, *memp, len); s += len; } else { free(buf); return (NSS_STR_PARSE_ERANGE); } } } dlen = s - buf; /* * if asked, return the data in /etc file format */ if (be->return_string_data == 1) { /* reset the result ptr to the original value */ argp->buf.result = NULL; if (dlen > argp->buf.buflen) { parsestat = NSS_STR_PARSE_ERANGE; } else { (void) strncpy(argp->buf.buffer, buf, dlen); argp->returnval = argp->buf.buffer; argp->returnlen = dlen; parsestat = NSS_SUCCESS; } } else { parsestat = (*argp->str2ent)(buf, dlen, argp->buf.result, argp->buf.buffer, argp->buf.buflen); } free(buf); return (parsestat); } static compat_backend_op_t group_ops[] = { _nss_compat_destr, _nss_compat_endent, _nss_compat_setent, _nss_compat_getent, getbyname, getbygid, getbymember }; /*ARGSUSED*/ nss_backend_t * _nss_compat_group_constr(dummy1, dummy2, dummy3) const char *dummy1, *dummy2, *dummy3; { return (_nss_compat_constr(group_ops, sizeof (group_ops) / sizeof (group_ops[0]), GF_PATH, NSS_LINELEN_GROUP, &db_root, _nss_initf_group_compat, 0, get_grname, merge_grents)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * getpwent.c * * lib/nsswitch/compat/getpwent.c -- name-service-switch backend for getpwnam() * et al that does 4.x compatibility. It looks in /etc/passwd; if it finds * passwd entries there that begin with "+" or "-", it consults other * services. By default it uses NIS (YP), but the user can override this * with a "passwd_compat" entry in /etc/nsswitch.conf, e.g. * passwd_compat: ldap * * This code tries to produce the same results as the 4.x code, even when * the latter seems ill thought-out (mostly in the handling of netgroups, * "-", and the combination thereof). Bug-compatible, in other words. * Though we do try to be more reasonable about the format of "+" and "-" * entries here, i.e. you don't have to pad them with spurious colons and * bogus uid/gid values. * * Caveats: * - More than one source may be specified, with the usual switch semantics, * but having multiple sources here is definitely odd. * - People who recursively specify "compat" deserve what they get. * - Entries that begin with "+@" or "-@" are interpreted using * getnetgrent() and innetgr(), which use the "netgroup" entry in * /etc/nsswitch.conf. If the sources for "passwd_compat" and "netgroup" * differ, everything should work fine, but the semantics will be pretty * confusing. */ #include #include /* For PASSWD (pathname to passwd file) */ #include #include #include "compat_common.h" static DEFINE_NSS_DB_ROOT(db_root); static void _nss_initf_passwd_compat(p) nss_db_params_t *p; { p->name = NSS_DBNAM_PASSWD; p->config_name = NSS_DBNAM_PASSWD_COMPAT; p->default_config = NSS_DEFCONF_PASSWD_COMPAT; } /* * Validates passwd entry replacing uid/gid > MAXUID by ID_NOBODY. */ int validate_passwd_ids(char *line, int *linelenp, int buflen, int extra_chars) { char *linep, *limit, *uidp, *gidp; uid_t uid; gid_t gid; ulong_t uidl, gidl; int olduidlen, oldgidlen, idlen; int linelen = *linelenp, newlinelen; if (linelen == 0 || *line == '+' || *line == '-') return (NSS_STR_PARSE_SUCCESS); linep = line; limit = line + linelen; while (linep < limit && *linep++ != ':') /* skip username */ continue; while (linep < limit && *linep++ != ':') /* skip password */ continue; if (linep == limit) return (NSS_STR_PARSE_PARSE); uidp = linep; uidl = strtoul(uidp, (char **)&linep, 10); /* grab uid */ olduidlen = linep - uidp; if (++linep >= limit || olduidlen == 0) return (NSS_STR_PARSE_PARSE); gidp = linep; gidl = strtoul(gidp, (char **)&linep, 10); /* grab gid */ oldgidlen = linep - gidp; if (linep >= limit || oldgidlen == 0) return (NSS_STR_PARSE_PARSE); if (uidl <= MAXUID && gidl <= MAXUID) return (NSS_STR_PARSE_SUCCESS); uid = (uidl > MAXUID) ? UID_NOBODY : (uid_t)uidl; gid = (gidl > MAXUID) ? GID_NOBODY : (gid_t)gidl; /* Check if we have enough space in the buffer */ idlen = snprintf(NULL, 0, "%u:%u", uid, gid); newlinelen = linelen + idlen - olduidlen - oldgidlen - 1; if (newlinelen + extra_chars > buflen) return (NSS_STR_PARSE_ERANGE); /* Replace ephemeral ids by ID_NOBODY */ (void) bcopy(linep, uidp + idlen, limit - linep + extra_chars); (void) snprintf(uidp, idlen + 1, "%u:%u", uid, gid); *(uidp + idlen) = ':'; /* restore : that was overwritten by snprintf */ *linelenp = newlinelen; return (NSS_STR_PARSE_SUCCESS); } static const char * get_pwname(argp) nss_XbyY_args_t *argp; { struct passwd *p = (struct passwd *)argp->returnval; return (p->pw_name); } static int check_pwname(argp) nss_XbyY_args_t *argp; { struct passwd *p = (struct passwd *)argp->returnval; return (strcmp(p->pw_name, argp->key.name) == 0); } static nss_status_t getbyname(be, a) compat_backend_ptr_t be; void *a; { nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; return (_nss_compat_XY_all(be, argp, check_pwname, NSS_DBOP_PASSWD_BYNAME)); } static int check_pwuid(argp) nss_XbyY_args_t *argp; { struct passwd *p = (struct passwd *)argp->returnval; return (p->pw_uid == argp->key.uid); } static nss_status_t getbyuid(be, a) compat_backend_ptr_t be; void *a; { nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; if (argp->key.uid > MAXUID) return (NSS_NOTFOUND); return (_nss_compat_XY_all(be, argp, check_pwuid, NSS_DBOP_PASSWD_BYUID)); } /*ARGSUSED*/ static int merge_pwents(be, argp, fields) compat_backend_ptr_t be; nss_XbyY_args_t *argp; const char **fields; { struct passwd *pw = (struct passwd *)argp->buf.result; char *buf = malloc(NSS_LINELEN_PASSWD); char *s; int parsestat; int len; int buflen; if (buf == 0) { return (NSS_STR_PARSE_PARSE); /* Really "out of memory", but PARSE_PARSE will have to do */ } /* * Don't allow overriding of * - username * - uid * - gid * That's what the SunOS 4.x code did; who are we to question it... */ s = buf; buflen = argp->buf.buflen; if (fields[1] != 0) len = snprintf(s, buflen, "%s:%s", pw->pw_name, fields[1]); else { /* ====> Does this do the right thing? */ if (pw->pw_age != 0 && *pw->pw_age != '\0') len = snprintf(s, buflen, "%s:%s,%s", pw->pw_name, pw->pw_passwd, pw->pw_age); else len = snprintf(s, buflen, "%s:%s", pw->pw_name, pw->pw_passwd); } if (len > buflen) return (NSS_STR_PARSE_ERANGE); s += len; buflen -= len; len = snprintf(s, buflen, ":%u:%u:%s:%s:%s", pw->pw_uid, pw->pw_gid, fields[4] != 0 ? fields[4] : pw->pw_gecos, fields[5] != 0 ? fields[5] : pw->pw_dir, fields[6] != 0 ? fields[6] : pw->pw_shell); if (len > buflen) return (NSS_STR_PARSE_ERANGE); s += len; len = s - buf; /* * if asked, return the data in /etc file format */ if (be->return_string_data == 1) { /* reset the result ptr to the original value */ argp->buf.result = NULL; if (len > argp->buf.buflen) { parsestat = NSS_STR_PARSE_ERANGE; } else { (void) strncpy(argp->buf.buffer, buf, len); argp->returnval = argp->buf.buffer; argp->returnlen = len; parsestat = NSS_SUCCESS; } } else { parsestat = (*argp->str2ent)(buf, len, argp->buf.result, argp->buf.buffer, argp->buf.buflen); } free(buf); return (parsestat); } static compat_backend_op_t passwd_ops[] = { _nss_compat_destr, _nss_compat_endent, _nss_compat_setent, _nss_compat_getent, getbyname, getbyuid }; /*ARGSUSED*/ nss_backend_t * _nss_compat_passwd_constr(dummy1, dummy2, dummy3) const char *dummy1, *dummy2, *dummy3; { return (_nss_compat_constr(passwd_ops, sizeof (passwd_ops) / sizeof (passwd_ops[0]), PASSWD, NSS_LINELEN_PASSWD, &db_root, _nss_initf_passwd_compat, 1, get_pwname, merge_pwents)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * getspent.c * * lib/nsswitch/compat/getspent.c -- name-service-switch backend for getspnam() * It looks in /etc/shadow; if it finds shadow entries there that begin * with "+" or "-", it consults other services. By default it uses NIS (YP), * but the user can override this with a "passwd_compat" entry in * /etc/nsswitch.conf, e.g. * passwd_compat: ldap * The main criterion for this code is that it behave in the same way as * the code for getpwnam() and friends (in getpwent.c). Note that it uses * the same nsswitch.conf entry, not a separate entry for "shadow_compat". * * Caveats: * - More than one source may be specified, with the usual switch semantics, * but having multiple sources here is definitely odd. * - People who recursively specify "compat" deserve what they get. * - Entries that begin with "+@" or "-@" are interpreted using * getnetgrent() and innetgr(), which use the "netgroup" entry in * /etc/nsswitch.conf. If the sources for "passwd_compat" and "netgroup" * differ, everything should work fine, but the semantics will be pretty * confusing. */ #include #include #include #include "compat_common.h" static DEFINE_NSS_DB_ROOT(db_root); static void _nss_initf_shadow_compat(p) nss_db_params_t *p; { p->name = NSS_DBNAM_SHADOW; p->config_name = NSS_DBNAM_PASSWD_COMPAT; p->default_config = NSS_DEFCONF_PASSWD_COMPAT; } static const char * get_spnamp(argp) nss_XbyY_args_t *argp; { struct spwd *s = (struct spwd *)argp->returnval; return (s->sp_namp); } static int check_spnamp(argp) nss_XbyY_args_t *argp; { struct spwd *s = (struct spwd *)argp->returnval; return (strcmp(s->sp_namp, argp->key.name) == 0); } static nss_status_t getbyname(be, a) compat_backend_ptr_t be; void *a; { nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; return (_nss_compat_XY_all(be, argp, check_spnamp, NSS_DBOP_SHADOW_BYNAME)); } /*ARGSUSED*/ static int merge_spents(be, argp, fields) compat_backend_ptr_t be; nss_XbyY_args_t *argp; const char **fields; { struct spwd *sp = (struct spwd *)argp->buf.result; /* * Don't allow overriding of the username; apart from that, * anything is fair game. */ if (fields[1] != 0) { size_t namelen = strlen(sp->sp_namp) + 1; size_t passlen = strlen(fields[1]) + 1; /* ===> Probably merits an explanation... */ if (namelen + passlen > argp->buf.buflen) { return (NSS_STR_PARSE_ERANGE); } if (sp->sp_namp != argp->buf.buffer) { (void) memmove(argp->buf.buffer, sp->sp_namp, namelen); sp->sp_namp = argp->buf.buffer; } (void) memcpy(argp->buf.buffer + namelen, fields[1], passlen); } #define override(field, longp) \ if ((field) != 0) { \ char *end; \ long val = strtol(field, &end, 10); \ \ if (*end == '\0') { \ *(longp) = val; \ } else { \ return (NSS_STR_PARSE_PARSE); \ } \ } /* do not override last changed date, it never gets reset. */ /* override(fields[2], &sp->sp_lstchg); */ override(fields[3], &sp->sp_min); override(fields[4], &sp->sp_max); override(fields[5], &sp->sp_warn); override(fields[6], &sp->sp_inact); override(fields[7], &sp->sp_expire); override(fields[8], &sp->sp_flag); /* * if asked, return the data in /etc file format */ if (be->return_string_data == 1) { int n; char b[16 * 7]; /* reset the result ptr to the original value */ argp->buf.result = NULL; #define printnum(i, num) \ sprintf(b + (i * 16), "%d", num)) ? b + (i * 16) : "" n = snprintf(argp->buf.buffer, argp->buf.buflen, "%s:%s:%s:%s:%s:%s:%s:%s:%s", sp->sp_namp, (sp->sp_pwdp ? sp->sp_pwdp : ""), (sp->sp_lstchg >= 0 && printnum(0, sp->sp_lstchg), (sp->sp_min >= 0 && printnum(1, sp->sp_min), (sp->sp_max >= 0 && printnum(2, sp->sp_max), (sp->sp_warn > 0 && printnum(3, sp->sp_warn), (sp->sp_inact > 0 && printnum(4, sp->sp_inact), (sp->sp_expire > 0 && printnum(5, sp->sp_expire), (sp->sp_flag != 0 && printnum(6, sp->sp_flag)); if (n > argp->buf.buflen) return (NSS_STR_PARSE_ERANGE); else { argp->returnlen = n - 1; return (NSS_SUCCESS); } } else return (NSS_STR_PARSE_SUCCESS); } static compat_backend_op_t shadow_ops[] = { _nss_compat_destr, _nss_compat_endent, _nss_compat_setent, _nss_compat_getent, getbyname }; /*ARGSUSED*/ nss_backend_t * _nss_compat_shadow_constr(dummy1, dummy2, dummy3) const char *dummy1, *dummy2, *dummy3; { return (_nss_compat_constr(shadow_ops, sizeof (shadow_ops) / sizeof (shadow_ops[0]), SHADOW, NSS_LINELEN_SHADOW, &db_root, _nss_initf_shadow_compat, 1, get_spnamp, merge_spents)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include "compat_common.h" static DEFINE_NSS_DB_ROOT(db_root); static void _nss_initf_userattr_compat(nss_db_params_t *p) { p->name = NSS_DBNAM_USERATTR; p->config_name = NSS_DBNAM_PASSWD_COMPAT; p->default_config = NSS_DEFCONF_PASSWD_COMPAT; } static const char * get_username(nss_XbyY_args_t *argp) { userstr_t *user = (userstr_t *)argp->returnval; return (user->name); } static int check_name(nss_XbyY_args_t *argp) { userstr_t *user = (userstr_t *)argp->returnval; const char *name = argp->key.name; #ifdef DEBUG (void) fprintf(stdout, "\n[getuserattr.c: check_name %s with %s]\n", user->name, name); #endif /* DEBUG */ if (strcmp(user->name, name) == 0) { return (1); } return (0); } static nss_status_t getbynam(compat_backend_ptr_t be, void *a) { nss_status_t res; nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; #ifdef DEBUG (void) fprintf(stdout, "\n[getuserattr.c: getbynam]\n"); #endif /* DEBUG */ res = _attrdb_compat_XY_all(be, argp, 1, check_name, NSS_DBOP_USERATTR_BYNAME); return (res); } static compat_backend_op_t userattr_ops[] = { _nss_compat_destr, _nss_compat_endent, _nss_compat_setent, _nss_compat_getent, getbynam }; /*ARGSUSED*/ nss_backend_t * _nss_compat_user_attr_constr(const char *dummy1, const char *dummy2, const char *dummy3, const char *dummy4, const char *dummy5) { return (_nss_compat_constr(userattr_ops, sizeof (userattr_ops)/sizeof (userattr_ops[0]), USERATTR_FILENAME, NSS_LINELEN_USERATTR, &db_root, _nss_initf_userattr_compat, 0, get_username, NULL)); } # # Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (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 # # Generic interface definition for usr/src/lib/nsswitch/compat. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION SUNWprivate_1.1 { global: _nss_compat_audit_user_constr; _nss_compat_group_constr; _nss_compat_passwd_constr; _nss_compat_shadow_constr; _nss_compat_user_attr_constr; local: *; };