/* * 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. */ /* * nis_common.c * * Common code and structures used by name-service-switch "nis" backends. */ #include "nis_common.h" #include #include #include #include #include #include #include #include #ifndef MT_UNSAFE_YP /* Is the libnsl YP client code MT-unsafe? */ #define MT_UNSAFE_YP 0 /* No, not any longer */ #endif #if MT_UNSAFE_YP static mutex_t one_lane = DEFAULTMUTEX; #endif /* uses (char *) where it should use (const char *) */ typedef char *grrr; /* * The YP client code thinks it's being helpful by appending '\n' and '\0' * to the values returned by yp_match() et al. In order to do this it * ends up doing more malloc()ing and data copying than would otherwise * be necessary. If we're interested in performance we should provide * alternative library interfaces that skip the helpfulness and instead * let the XDR routines dump the value directly into the buffer where * we really want it. For now, though, we just use the vanilla interface. */ static nss_status_t switch_err(ypstatus, ismatch) int ypstatus; int ismatch; { switch (ypstatus) { case 0: errno = 0; return (NSS_SUCCESS); case YPERR_BADARGS: case YPERR_KEY: errno = 0; return (NSS_NOTFOUND); /* * When the YP server is running in DNS forwarding mode, * the forwarder will return YPERR_NOMORE to us if it * is unable to contact a server (i.e., it has timed out). * The NSS_NISSERVDNS_TRYAGAIN is returned for timeout errors. */ case YPERR_NOMORE: if (ismatch) return (NSS_NISSERVDNS_TRYAGAIN); else return (NSS_NOTFOUND); case YPERR_DOMAIN: case YPERR_YPSERV: case YPERR_BUSY: return (NSS_TRYAGAIN); default: return (NSS_UNAVAIL); } } /*ARGSUSED*/ nss_status_t _nss_nis_setent(be, dummy) nis_backend_ptr_t be; void *dummy; { if (be->enum_key != 0) { free(be->enum_key); be->enum_key = 0; } be->enum_keylen = 0; return (NSS_SUCCESS); } nss_status_t _nss_nis_endent(be, dummy) nis_backend_ptr_t be; void *dummy; { return (_nss_nis_setent(be, dummy)); /* Nothing else we can clean up, is there? */ } void massage_netdb(const char **valp, int *vallenp) { const char *first; const char *last; const char *val = *valp; int vallen = *vallenp; if ((last = memchr(val, '#', vallen)) == 0) { last = val + vallen; } for (first = val; first < last && isspace(*first); first++) { ; } for (/* cstyle */; first < last && isspace(last[-1]); last--) { ; } /* * Don't check for an empty line because it shouldn't ever * have made it into the YP map. */ *valp = first; *vallenp = (int)(last - first); } nss_status_t _nss_nis_ypmatch(domain, map, key, valp, vallenp, ypstatusp) const char *domain; const char *map; const char *key; char **valp; int *vallenp; int *ypstatusp; { int ypstatus; #if MT_UNSAFE_YP sigset_t oldmask, newmask; (void) sigfillset(&newmask); (void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); (void) mutex_lock(&one_lane); #endif ypstatus = __yp_match_cflookup((grrr)domain, (grrr)map, (grrr)key, (int)strlen(key), valp, vallenp, 0); #if MT_UNSAFE_YP (void) mutex_unlock(&one_lane); (void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); #endif if (ypstatusp != 0) { *ypstatusp = ypstatus; } return (switch_err(ypstatus, 1)); } /* * XXX special version of _nss_nis_ypmatch() for handling C2 (passwd.adjunct) * lookups when we need a reserved port. */ static nss_status_t _nss_nis_ypmatch_rsvdport(domain, map, key, valp, vallenp, ypstatusp) const char *domain; const char *map; const char *key; char **valp; int *vallenp; int *ypstatusp; { int ypstatus; #if MT_UNSAFE_YP sigset_t oldmask, newmask; (void) sigfillset(&newmask); (void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); (void) mutex_lock(&one_lane); #endif ypstatus = __yp_match_rsvdport_cflookup((grrr)domain, (grrr)map, (grrr)key, strlen(key), valp, vallenp, 0); #if MT_UNSAFE_YP (void) mutex_unlock(&one_lane); (void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); #endif if (ypstatusp != 0) { *ypstatusp = ypstatus; } return (switch_err(ypstatus, 1)); } nss_status_t _nss_nis_lookup(be, args, netdb, map, key, ypstatusp) nis_backend_ptr_t be; nss_XbyY_args_t *args; int netdb; const char *map; const char *key; int *ypstatusp; { nss_status_t res; int vallen; char *val; char *free_ptr; int parsestat; if ((res = _nss_nis_ypmatch(be->domain, map, key, &val, &vallen, ypstatusp)) != NSS_SUCCESS) { return (res); } parsestat = NSS_STR_PARSE_SUCCESS; if (strcmp(map, "passwd.byname") == 0 || strcmp(map, "passwd.byuid") == 0) { parsestat = validate_passwd_ids(&val, &vallen, 1); } else if (strcmp(map, "group.byname") == 0) parsestat = validate_group_ids(&val, &vallen, 1); if (parsestat != NSS_STR_PARSE_SUCCESS) { free(val); return (NSS_NOTFOUND); } free_ptr = val; if (netdb) { massage_netdb((const char **)&val, &vallen); } args->returnval = NULL; args->returnlen = 0; parsestat = (*args->str2ent)(val, vallen, args->buf.result, args->buf.buffer, args->buf.buflen); if (parsestat == NSS_STR_PARSE_SUCCESS) { args->returnval = args->buf.result; args->returnlen = vallen; res = NSS_SUCCESS; } else if (parsestat == NSS_STR_PARSE_ERANGE) { args->erange = 1; /* We won't find this otherwise, anyway */ res = NSS_NOTFOUND; } /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */ free(free_ptr); return (res); } nss_status_t _nss_nis_lookup_rsvdport(be, args, netdb, map, key, ypstatusp) nis_backend_ptr_t be; nss_XbyY_args_t *args; int netdb; const char *map; const char *key; int *ypstatusp; { nss_status_t res; int vallen; char *val; char *free_ptr; int parsestat; if ((res = _nss_nis_ypmatch_rsvdport(be->domain, map, key, &val, &vallen, ypstatusp)) != NSS_SUCCESS) { return (res); } free_ptr = val; if (netdb) { massage_netdb((const char **)&val, &vallen); } args->returnval = NULL; args->returnlen = 0; parsestat = (*args->str2ent)(val, vallen, args->buf.result, args->buf.buffer, args->buf.buflen); if (parsestat == NSS_STR_PARSE_SUCCESS) { args->returnval = args->buf.result; args->returnlen = vallen; res = NSS_SUCCESS; } else if (parsestat == NSS_STR_PARSE_ERANGE) { args->erange = 1; /* We won't find this otherwise, anyway */ res = NSS_NOTFOUND; } /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */ free(free_ptr); return (res); } static nss_status_t do_getent(be, args, netdb) nis_backend_ptr_t be; nss_XbyY_args_t *args; int netdb; { nss_status_t res; int ypstatus; int outkeylen, outvallen; char *outkey, *outval; char *free_ptr; int parsestat; #if MT_UNSAFE_YP sigset_t oldmask, newmask; (void) sigfillset(&newmask); (void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); (void) mutex_lock(&one_lane); #endif if (be->enum_key == 0) { ypstatus = __yp_first_cflookup((grrr)be->domain, (grrr)be->enum_map, &outkey, &outkeylen, &outval, &outvallen, 0); } else { ypstatus = __yp_next_cflookup((grrr)be->domain, (grrr)be->enum_map, be->enum_key, be->enum_keylen, &outkey, &outkeylen, &outval, &outvallen, 0); } #if MT_UNSAFE_YP (void) mutex_unlock(&one_lane); (void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); #endif if ((res = switch_err(ypstatus, 0)) != NSS_SUCCESS) { return (res); } free_ptr = outval; if (netdb) { massage_netdb((const char **)&outval, &outvallen); } args->returnval = NULL; args->returnlen = 0; parsestat = (*args->str2ent)(outval, outvallen, args->buf.result, args->buf.buffer, args->buf.buflen); if (parsestat == NSS_STR_PARSE_SUCCESS) { args->returnval = args->buf.result; args->returnlen = outvallen; res = NSS_SUCCESS; } else if (parsestat == NSS_STR_PARSE_ERANGE) { args->erange = 1; /* We won't find this otherwise, anyway */ res = NSS_NOTFOUND; } /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */ free(free_ptr); if (be->enum_key != 0) { free(be->enum_key); } be->enum_key = outkey; be->enum_keylen = outkeylen; return (res); } nss_status_t _nss_nis_getent_rigid(be, args) nis_backend_ptr_t be; void *args; { return (do_getent(be, (nss_XbyY_args_t *)args, 0)); } nss_status_t _nss_nis_getent_netdb(be, args) nis_backend_ptr_t be; void *args; { return (do_getent(be, (nss_XbyY_args_t *)args, 1)); } struct cb_data { void *args; const char *filter; nis_do_all_func_t func; nss_status_t result; }; enum { ITER_NEXT = 0, ITER_STOP = 1 }; /* Should be in */ /*ARGSUSED*/ static int do_cback(instatus, inkey, inkeylen, inval, invallen, indata) int instatus; const char *inkey; int inkeylen; const char *inval; int invallen; struct cb_data *indata; { nss_status_t res; if (instatus != YP_TRUE) { return (ITER_NEXT); /* yp_all may decide otherwise... */ } if (indata->filter != 0 && strstr(inval, indata->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. */ return (ITER_NEXT); } res = (*indata->func)(inval, invallen, indata->args); if (res == NSS_NOTFOUND) { return (ITER_NEXT); } else { indata->result = res; return (ITER_STOP); } } nss_status_t _nss_nis_do_all(be, args, filter, func) nis_backend_ptr_t be; void *args; const char *filter; nis_do_all_func_t func; { int ypall_status; struct cb_data data; struct ypall_callback cback; data.args = args; data.filter = filter; data.func = func; data.result = NSS_NOTFOUND; cback.foreach = do_cback; cback.data = (char *)&data; #if MT_UNSAFE_YP sigset_t oldmask, newmask; (void) sigfillset(&newmask); (void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); (void) mutex_lock(&one_lane); #endif ypall_status = __yp_all_cflookup((grrr)be->domain, (grrr) be->enum_map, &cback, 0); #if MT_UNSAFE_YP (void) mutex_unlock(&one_lane); (void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); #endif switch (ypall_status) { case 0: return (data.result); case YPERR_DOMAIN: case YPERR_YPSERV: case YPERR_BUSY: /* Probably never get this, but... */ return (NSS_TRYAGAIN); default: return (NSS_UNAVAIL); } } struct XbyY_data { nss_XbyY_args_t *args; nis_XY_check_func func; int netdb; }; static nss_status_t XbyY_iterator(instr, instr_len, a) const char *instr; int instr_len; void *a; { struct XbyY_data *xydata = (struct XbyY_data *)a; nss_XbyY_args_t *args = xydata->args; nss_status_t res; int parsestat; if (xydata->netdb) { massage_netdb(&instr, &instr_len); } args->returnval = NULL; args->returnlen = 0; parsestat = (*args->str2ent)(instr, instr_len, args->buf.result, args->buf.buffer, args->buf.buflen); if (parsestat == NSS_STR_PARSE_SUCCESS) { args->returnval = args->buf.result; if ((*xydata->func)(args)) { res = NSS_SUCCESS; args->returnlen = instr_len; } else { res = NSS_NOTFOUND; args->returnval = 0; } } else if (parsestat == NSS_STR_PARSE_ERANGE) { /* * If we got here because (*str2ent)() found that the buffer * wasn't big enough, maybe we should quit and return erange. * Instead we'll keep looking and eventually return "not * found" -- it's a bug, but not an earth-shattering one. */ args->erange = 1; /* <== Is this a good idea? */ res = NSS_NOTFOUND; } /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */ return (res); } nss_status_t _nss_nis_XY_all(be, args, netdb, filter, func) nis_backend_ptr_t be; nss_XbyY_args_t *args; int netdb; const char *filter; nis_XY_check_func func; { struct XbyY_data data; data.args = args; data.func = func; data.netdb = netdb; return (_nss_nis_do_all(be, &data, filter, XbyY_iterator)); /* Now how many levels of callbacks was that? */ } /*ARGSUSED*/ nss_status_t _nss_nis_destr(be, dummy) nis_backend_ptr_t be; void *dummy; { if (be != 0) { /* === Should change to invoke ops[ENDENT] ? */ (void) _nss_nis_endent(be, 0); free(be); } return (NSS_SUCCESS); /* In case anyone is dumb enough to check */ } /* We want to lock this even if the YP routines are MT-safe */ static mutex_t yp_domain_lock = DEFAULTMUTEX; static char *yp_domain; const char * _nss_nis_domain() { char *domain; /* * This much locking is probably more "by the book" than necessary... */ sigset_t oldmask, newmask; (void) sigfillset(&newmask); (void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); (void) mutex_lock(&yp_domain_lock); if ((domain = yp_domain) == 0) { #if MT_UNSAFE_YP (void) mutex_lock(&one_lane); #endif if (yp_get_default_domain(&yp_domain) == 0) { domain = yp_domain; } #if MT_UNSAFE_YP (void) mutex_unlock(&one_lane); #endif } (void) mutex_unlock(&yp_domain_lock); (void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); return (domain); } nss_backend_t * _nss_nis_constr(ops, n_ops, enum_map) nis_backend_op_t ops[]; int n_ops; const char *enum_map; { const char *domain; nis_backend_ptr_t be; if ((domain = _nss_nis_domain()) == 0 || (be = (nis_backend_ptr_t)malloc(sizeof (*be))) == 0) { return (0); } be->ops = ops; be->n_ops = n_ops; be->domain = domain; be->enum_map = enum_map; /* Don't strdup, assume valid forever */ be->enum_key = 0; be->enum_keylen = 0; return ((nss_backend_t *)be); } /* * This routine is used to parse lines of the form: * name number aliases * It returns 1 if the key in argp matches any one of the * names in the line, otherwise 0 * Used by rpc */ int _nss_nis_check_name_aliases(nss_XbyY_args_t *argp, const char *line, int linelen) { const char *limit, *linep, *keyp; linep = line; limit = line + linelen; keyp = argp->key.name; /* compare name */ while (*keyp && linep < limit && !isspace(*linep) && *keyp == *linep) { keyp++; linep++; } if (*keyp == '\0' && linep < limit && isspace(*linep)) return (1); /* skip remainder of the name, if any */ while (linep < limit && !isspace(*linep)) linep++; /* skip the delimiting spaces */ while (linep < limit && isspace(*linep)) linep++; /* compare with the aliases */ while (linep < limit) { /* * 1st pass: skip number * Other passes: skip remainder of the alias name, if any */ while (linep < limit && !isspace(*linep)) linep++; /* skip the delimiting spaces */ while (linep < limit && isspace(*linep)) linep++; /* compare with the alias name */ keyp = argp->key.name; while (*keyp && linep < limit && !isspace(*linep) && *keyp == *linep) { keyp++; linep++; } if (*keyp == '\0' && (linep == limit || isspace(*linep))) return (1); } return (0); }