/* * 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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * This module contains the subroutines used by the server to manipulate * objects and names. */ #include "mt.h" #include #include #include #include #include #include #include #include #include #include #include #include /* Must be ahead of rpcb_clnt.h */ #include #include #include #include #include #include #include #include #include "nis_clnt.h" #include #include "nis_local.h" #include #define MAXIPRINT (11) /* max length of printed integer */ static char *PKTABLE = "cred.org_dir"; #define PKTABLE_LEN 12 /* * send and receive buffer size used for clnt_tli_create if not specified. * This is only used for "UDP" connection. * This limit can be changed from the application if this value is too * small for the application. To use the maximum value for the transport, * set this value to 0. */ int __nisipbufsize = 8192; /* Error result returned by nis_make_error() when malloc fails */ const nis_result __nomem_nis_result = {NIS_NOMEMORY, {0, 0}, {0, 0}, 0, 0, 0, 0}; extern int __readColdStartFile(); /* * Static function prototypes. */ static struct local_names *__get_local_names(void); static char *__map_addr(struct netconfig *, char *, rpcprog_t, rpcvers_t); CLIENT * nis_make_rpchandle_uaddr(nis_server *, int, rpcprog_t, rpcvers_t, uint_t, int, int, char *); #define COMMA ',' /* Avoid cstyle bug */ /* __nis_data_directory is READ ONLY, so no locking is needed */ /* Note: We make it static, so external caller can not access it */ /* i.e we make sure it stay read only */ static char __nis_data_directory[1024] = {"/var/nis/"}; /* These macros make the code easier to read */ #ifdef NOTIME #define __start_clock(n) #define __stop_clock(n) n #else static struct timeval clocks[MAXCLOCKS]; #define LOOP_UADDR "127.0.0.1.0.0" /* * __start_clock() * * This function will start the "stopwatch" on the function calls. * It uses an array of time vals to keep track of the time. The * sister call __stop_clock() will return the number of microseconds * since the clock was started. This allows for keeping statistics * on the NIS calls and tuning the service. If the clock in question * is not "stopped" this function returns an error. */ int __start_clock( int clk) /* The clock we want to start */ { if ((clk >= MAXCLOCKS) || (clk < 0) || (clocks[clk].tv_sec)) return (FALSE); (void) gettimeofday(&clocks[clk], NULL); return (TRUE); } uint32_t __stop_clock(int clk) { struct timeval now; uint32_t secs, micros; if ((clk >= MAXCLOCKS) || (clk < 0) || (!clocks[clk].tv_sec)) return (0); (void) gettimeofday(&now, NULL); secs = (int)(now.tv_sec - clocks[clk].tv_sec); if (now.tv_usec < clocks[clk].tv_usec) { micros = (int)((now.tv_usec + 1000000) - clocks[clk].tv_usec); secs--; /* adjusted 'cuz we added a second above */ } else { micros = (int)(now.tv_usec - clocks[clk].tv_usec); } micros = micros + (secs * 1000000); /* All micros now */ clocks[clk].tv_sec = 0; /* Stop the clock. */ return (micros); } #endif /* no time */ /* * nis_dir_cmp() -- the results can be read as: * "Name 'n1' is a $result than name 'n2'" */ name_pos nis_dir_cmp( nis_name n1, nis_name n2) /* See if these are the same domain */ { size_t l1, l2; name_pos result; if ((n1 == NULL) || (n2 == NULL)) return (BAD_NAME); l1 = strlen(n1); l2 = strlen(n2); /* In this routine we're lenient and don't require a trailing '.' */ /* so we need to ignore it if it does appear. */ /* ==== That's what the previous version did so this one does */ /* too, but why? Is this inconsistent with rest of system? */ if (l1 != 0 && n1[l1 - 1] == '.') { --l1; } if (l2 != 0 && n2[l2 - 1] == '.') { --l2; } if (l1 > l2) { result = LOWER_NAME; } else if (l1 == l2) { result = SAME_NAME; } else /* (l1 < l2); swap l1/l2 and n1/n2 */ { nis_name ntmp; size_t ltmp; ntmp = n1; n1 = n2; n2 = ntmp; ltmp = l1; l1 = l2; l2 = ltmp; result = HIGHER_NAME; } /* Now l1 >= l2 in all cases */ if (l2 == 0) { /* Special case for n2 == "." or "" */ return (result); } if (l1 > l2) { n1 += l1 - l2; if (n1[-1] != '.') { return (NOT_SEQUENTIAL); } } if (strncasecmp(n1, n2, l2) == 0) { return (result); } return (NOT_SEQUENTIAL); } #define LN_BUFSIZE (size_t)1024 struct principal_list { uid_t uid; char principal[LN_BUFSIZE]; struct principal_list *next; }; struct local_names { char domain[LN_BUFSIZE]; char host[LN_BUFSIZE]; char *rpcdomain; struct principal_list *principal_map; char group[LN_BUFSIZE]; }; static mutex_t ln_lock = DEFAULTMUTEX; /* lock level 2 */ static struct local_names *ln = NULL; static struct local_names *__get_local_names1(); static struct local_names * __get_local_names(void) { struct local_names *names; sig_mutex_lock(&ln_lock); names = __get_local_names1(); sig_mutex_unlock(&ln_lock); return (names); } static char * get_nis_domain(void) { directory_obj dobj; enum __nsw_parse_err pserr; struct __nsw_switchconfig *conf; static int checked_domain = 0; static char *nisdomain = 0; if (!checked_domain) { checked_domain = 1; /* * Check that nisplus is first in nsswitch.conf for publickey. */ conf = __nsw_getconfig("publickey", &pserr); if (conf == NULL) return (NULL); if (conf->num_lookups <= 0) return (NULL); if (strcasecmp(conf->lookups[0].service_name, "nisplus") != 0) return (NULL); /* * Read cold-start file to determine directory where * the machine's credentials are stored. */ if (!__readColdStartFile(&dobj)) return (NULL); nisdomain = strdup(dobj.do_name); xdr_free((xdrproc_t)xdr_directory_obj, (char *)&dobj); } return (nisdomain); } static struct local_names * __get_local_names1(void) { char *t; if (ln != NULL) { /* Second and subsequent calls go this way */ return (ln); } /* First call goes this way */ ln = calloc(1, sizeof (*ln)); if (ln == NULL) { syslog(LOG_ERR, "__get_local_names: Out of heap."); return (NULL); } ln->principal_map = NULL; if (sysinfo(SI_SRPC_DOMAIN, ln->domain, LN_BUFSIZE) < 0) return (ln); /* If no dot exists, add one. */ if (ln->domain[strlen(ln->domain)-1] != '.') (void) strcat(ln->domain, "."); if (sysinfo(SI_HOSTNAME, ln->host, LN_BUFSIZE) < 0) return (ln); /* * Check for fully qualified hostname. If it's a fully qualified * hostname, strip off the domain part. We always use the local * domainname for the host principal name. */ t = strchr(ln->host, '.'); if (t) *t = 0; if (ln->domain[0] != '.') (void) strcat(ln->host, "."); if ((ln->rpcdomain = get_nis_domain()) != NULL) { (void) strcat(ln->host, ln->rpcdomain); } else { ln->rpcdomain = strdup(ln->domain); (void) strcat(ln->host, ln->domain); } t = getenv("NIS_GROUP"); if (t == NULL) { ln->group[0] = '\0'; } else { size_t maxlen = LN_BUFSIZE-1; /* max chars to copy */ char *temp; /* temp marker */ /* * Copy <= maximum characters from NIS_GROUP; strncpy() * doesn't terminate, so we do that manually. #1223323 * Also check to see if it's "". If it's the null string, * we return because we don't want to add ".domain". */ (void) strncpy(ln->group, t, maxlen); if (strcmp(ln->group, "") == 0) { return (ln); } ln->group[maxlen] = '\0'; /* Is the group name somewhat fully-qualified? */ temp = strrchr(ln->group, '.'); /* If not, we need to add ".domain" to the group */ if ((temp == NULL) || (temp[1] != '\0')) { /* truncate to make room for ".domain" */ ln->group[maxlen - (strlen(ln->domain)+1)] = '\0'; /* concat '.' if domain doesn't already have it */ if (ln->domain[0] != '.') { (void) strcat(ln->group, "."); } (void) strcat(ln->group, ln->domain); } } return (ln); } /* * nis_local_group() * * Return's the group name of the current user. */ nis_name nis_local_group(void) { struct local_names *ln = __get_local_names(); /* LOCK NOTE: Warning, after initialization, "ln" is expected */ /* to stay constant, So no need to lock here. If this assumption */ /* is changed, this code must be protected. */ if (!ln) return (NULL); return (ln->group); } /* * __nis_nextsep_of() * * This internal funtion will accept a pointer to a NIS name string and * return a pointer to the next separator occurring in it (it will point * just past the first label). It allows for labels to be "quoted" to * prevent the the dot character within them to be interpreted as a * separator, also the quote character itself can be quoted by using * it twice. If the the name contains only one label and no trailing * dot character, a pointer to the terminating NULL is returned. */ nis_name __nis_nextsep_of(char *s) { char *d; int in_quotes = FALSE, quote_quote = FALSE; if (!s) return (NULL); for (d = s; (in_quotes && (*d != '\0')) || (!in_quotes && (*d != '.') && (*d != '\0')); d++) { if (quote_quote && in_quotes && (*d != '"')) { quote_quote = FALSE; in_quotes = FALSE; if (*d == '.') break; } else if (quote_quote && in_quotes && (*d == '"')) { quote_quote = FALSE; } else if (quote_quote && (*d != '"')) { quote_quote = FALSE; in_quotes = TRUE; } else if (quote_quote && (*d == '"')) { quote_quote = FALSE; } else if (in_quotes && (*d == '"')) { quote_quote = TRUE; } else if (!in_quotes && (*d == '"')) { quote_quote = TRUE; } } if (quote_quote || in_quotes) { syslog(LOG_DEBUG, "__nis_nextsep_of: " "Mismatched quotes in %s", s); } return (d); } /* * nis_domain_of() * * This internal funtion will accept a pointer to a NIS name string and * return a pointer to the "domain" part of it. * * ==== We don't need nis_domain_of_r(), but should we provide one for * uniformity? */ nis_name nis_domain_of(char *s) { char *d; d = __nis_nextsep_of(s); if (d == NULL) return (NULL); if (*d == '.') d++; if (*d == '\0') /* Don't return a zero length string */ return ("."); /* return root domain instead */ return (d); } /* * nis_leaf_of() * * Returns the first label of a name. (other half of __domain_of) */ nis_name nis_leaf_of_r( const nis_name s, char *buf, size_t bufsize) { size_t nchars; const char *d = __nis_nextsep_of((char *)s); if (d == 0) { return (0); } nchars = d - s; if (bufsize < nchars + 1) { return (0); } (void) strncpy(buf, s, nchars); buf[nchars] = '\0'; return (buf); } static pthread_key_t buf_key; static char buf_main[LN_BUFSIZE]; nis_name nis_leaf_of(char *s) { char *buf = thr_main()? buf_main : thr_get_storage(&buf_key, LN_BUFSIZE, free); if (buf == NULL) return (NULL); return (nis_leaf_of_r(s, buf, LN_BUFSIZE)); } /* * nis_name_of() * This internal function will remove from the NIS name, the domain * name of the current server, this will leave the unique part in * the name this becomes the "internal" version of the name. If this * function returns NULL then the name we were given to resolve is * bad somehow. * NB: Uses static storage and this is a no-no with threads. XXX */ nis_name nis_name_of_r( char *s, /* string with the name in it. */ char *buf, size_t bufsize) { char *d; struct local_names *ln = __get_local_names(); size_t dl, sl; name_pos p; #ifdef lint bufsize = bufsize; #endif /* lint */ if ((!s) || (!ln)) return (NULL); /* No string, this can't continue */ d = &(ln->domain[0]); dl = strlen(ln->domain); /* _always dot terminated_ */ sl = strlen(s); if (sl >= bufsize || (s[sl-1] != '.' && sl >= bufsize-1)) return (NULL); (void) strcpy(buf, s); /* Make a private copy of 's' */ if (buf[sl-1] != '.') { /* Add a dot if necessary. */ (void) strcat(buf, "."); sl++; } if (dl == 1) { /* We're the '.' directory */ buf[sl-1] = '\0'; /* Lose the 'dot' */ return (buf); } p = nis_dir_cmp(buf, d); /* 's' is above 'd' in the tree */ if ((p == HIGHER_NAME) || (p == NOT_SEQUENTIAL) || (p == SAME_NAME)) return (NULL); /* Insert a NUL where the domain name starts in the string */ buf[(sl - dl) - 1] = '\0'; /* Don't return a zero length name */ if (buf[0] == '\0') return (NULL); return (buf); } nis_name nis_name_of( char *s) /* string with the name in it. */ { char *buf = thr_main()? buf_main : thr_get_storage(&buf_key, LN_BUFSIZE, free); if (!buf) return (NULL); return (nis_name_of_r(s, buf, LN_BUFSIZE)); } /* * nis_local_directory() * * Return a pointer to a string with the local directory name in it. */ nis_name nis_local_directory(void) { struct local_names *ln = __get_local_names(); /* LOCK NOTE: Warning, after initialization, "ln" is expected */ /* to stay constant, So no need to lock here. If this assumption */ /* is changed, this code must be protected. */ if (ln == NULL) return (NULL); return (ln->domain); } /* * __nis_rpc_domain() * * Return a pointer to a string with the rpc domain name in it. */ nis_name __nis_rpc_domain() { struct local_names *ln = __get_local_names(); /* LOCK NOTE: Warning, after initialization, "ln" is expected */ /* to stay constant, So no need to lock here. If this assumption */ /* is changed, this code must be protected. */ if (ln == NULL) return (NULL); return (ln->rpcdomain); } /* * nis_getprincipal: * Return the prinicipal name of the given uid in string supplied. * Returns status obtained from nis+. * * Look up the LOCAL mapping in the local cred table. Note that if the * server calls this, then the version of nis_list that will * will be bound here is the 'safe' one in the server code. * * The USE_DGRAM + NO_AUTHINFO is required to prevent a * recursion through the getnetname() interface which is * called by authseccreate_pk and authdes_pk_create(). * * NOTE that if you really want to get the nis+ principal name, * you should not use this call. You should do something similar * but use an authenticated handle. */ int __nis_principal(char *principal_name, uid_t uid, char *directory) { nis_result *res; char buf[NIS_MAXNAMELEN]; int status; if ((strlen(directory)+MAXIPRINT+PKTABLE_LEN+32) > (size_t)NIS_MAXNAMELEN) { return (NIS_BADNAME); } (void) snprintf(buf, sizeof (buf), "[auth_name=%d,auth_type=LOCAL],%s.%s", (int)uid, PKTABLE, directory); if (buf[strlen(buf)-1] != '.') (void) strcat(buf, "."); res = nis_list(buf, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, NULL, NULL); status = res->status; if (status == NIS_SUCCESS || status == NIS_S_SUCCESS) { if (res->objects.objects_len > 1) { /* * More than one principal with same uid? * something wrong with cred table. Should be unique * Warn user and continue. */ syslog(LOG_ERR, "nis_principal: LOCAL entry for %d in directory %s not unique", uid, directory); } (void) strcpy(principal_name, ENTRY_VAL(res->objects.objects_val, 0)); } nis_freeresult(res); return (status); } /* * nis_local_principal() * Generate the principal name for this user by looking it up its LOCAL * entry in the cred table of the local direectory. * Does not use an authenticated call (to prevent recursion because * this is called by user2netname). * * NOTE: the principal strings returned by nis_local_principal are * never changed and never freed, so there is no need to copy them. * Also note that nis_local_principal can return NULL. */ nis_name nis_local_principal(void) { struct local_names *ln = __get_local_names(); uid_t uid; int status; char *dirname; static mutex_t local_principal_lock = DEFAULTMUTEX; struct principal_list *p; if (ln == NULL) return (NULL); sig_mutex_lock(&local_principal_lock); uid = geteuid(); p = ln->principal_map; while (p) { if (p->uid == uid) { ASSERT(*(p->principal) != 0); sig_mutex_unlock(&local_principal_lock); return (p->principal); } p = p->next; } if (uid == 0) { sig_mutex_unlock(&local_principal_lock); return (ln->host); } p = calloc(1, sizeof (*p)); if (p == NULL) return (NULL); if (!ln->principal_map) { ln->principal_map = p; } dirname = nis_local_directory(); if ((dirname == NULL) || (dirname[0] == NULL)) { (void) strcpy(p->principal, "nobody"); p->uid = uid; sig_mutex_unlock(&local_principal_lock); return (p->principal); } switch (status = __nis_principal(p->principal, uid, dirname)) { case NIS_SUCCESS: case NIS_S_SUCCESS: break; case NIS_NOTFOUND: case NIS_PARTIAL: case NIS_NOSUCHNAME: case NIS_NOSUCHTABLE: (void) strcpy(p->principal, "nobody"); break; default: /* * XXX We should return 'nobody', but * should we be remembering 'nobody' as our * principal name here? Some errors might be * transient. */ syslog(LOG_ERR, "nis_local_principal: %s", nis_sperrno(status)); (void) strcpy(p->principal, "nobody"); } p->uid = uid; sig_mutex_unlock(&local_principal_lock); return (p->principal); } /* * nis_local_host() * Generate the principal name for this host, "hostname"+"domainname" * unless the hostname already has "dots" in its name. */ nis_name nis_local_host(void) { struct local_names *ln = __get_local_names(); /* LOCK NOTE: Warning, after initialization, "ln" is expected */ /* to stay constant, So no need to lock here. If this assumption */ /* is changed, this code must be protected. */ if (ln == NULL) return (NULL); return (ln->host); } /* * nis_destroy_object() * This function takes a pointer to a NIS object and deallocates it. This * is the inverse of __clone_object below. It must be able to correctly * deallocate partially allocated objects because __clone_object will call * it if it runs out of memory and has to abort. Everything is freed, * INCLUDING the pointer that is passed. */ void nis_destroy_object(nis_object *obj) /* The object to clone */ { if (obj == 0) return; xdr_free(xdr_nis_object, (char *)obj); free(obj); } /* nis_destroy_object */ static void destroy_nis_sdata(void *p) { struct nis_sdata *ns = p; if (ns->buf != 0) free(ns->buf); free(ns); } /* XXX Why are these static ? */ /* static XDR in_xdrs, out_xdrs; */ /* * __clone_object_r() * This function takes a pointer to a NIS object and clones it. This * duplicate object is now available for use in the local context. */ nis_object * nis_clone_object_r( nis_object *obj, /* The object to clone */ nis_object *dest, /* Use this pointer if non-null */ struct nis_sdata *clone_buf_ptr) { nis_object *result; /* The clone itself */ int status; /* a counter variable */ XDR in_xdrs, out_xdrs; if (!nis_get_static_storage(clone_buf_ptr, 1, xdr_sizeof(xdr_nis_object, obj))) return (NULL); (void) memset(&in_xdrs, 0, sizeof (in_xdrs)); (void) memset(&out_xdrs, 0, sizeof (out_xdrs)); xdrmem_create(&in_xdrs, clone_buf_ptr->buf, clone_buf_ptr->size, XDR_ENCODE); xdrmem_create(&out_xdrs, clone_buf_ptr->buf, clone_buf_ptr->size, XDR_DECODE); /* Allocate a basic NIS object structure */ if (dest) { (void) memset(dest, 0, sizeof (nis_object)); result = dest; } else result = calloc(1, sizeof (nis_object)); if (result == NULL) return (NULL); /* Encode our object into the clone buffer */ (void) xdr_setpos(&in_xdrs, 0); status = xdr_nis_object(&in_xdrs, obj); if (status == FALSE) return (NULL); /* Now decode the buffer into our result pointer ... */ (void) xdr_setpos(&out_xdrs, 0); status = xdr_nis_object(&out_xdrs, result); if (status == FALSE) return (NULL); /* presto changeo, a new object */ return (result); } /* __clone_object_r */ nis_object * nis_clone_object( nis_object *obj, /* The object to clone */ nis_object *dest) /* Use this pointer if non-null */ { static pthread_key_t clone_buf_key; static struct nis_sdata clone_buf_main; struct nis_sdata *clone_buf_ptr; clone_buf_ptr = thr_main()? &clone_buf_main : thr_get_storage(&clone_buf_key, sizeof (struct nis_sdata), destroy_nis_sdata); return (nis_clone_object_r(obj, dest, clone_buf_ptr)); } /* __clone_object */ /* * __break_name() converts a NIS name into it's components, returns an * array of char pointers pointing to the components and INVERTS there * order so that they are root first, then down. The list is terminated * with a null pointer. Returned memory can be freed by freeing the last * pointer in the list and the pointer returned. */ char ** __break_name( nis_name name, int *levels) { char **pieces; /* pointer to the pieces */ char *s; /* Temporary */ char *data; /* actual data and first piece pointer. */ int components; /* Number of estimated components */ size_t namelen; /* Length of the original name. */ int i; /* First check to see that name is not NULL */ if (!name) return (NULL); if ((namelen = strlen(name)) == 0) return (NULL); /* Null string */ namelen = strlen(name); data = strdup(name); if (!data) return (NULL); /* No memory! */ /* Kill the optional trailing dot */ if (*(data+namelen-1) == '.') { *(data+namelen-1) = '\0'; namelen--; } s = data; components = 1; while (*s != '\0') { if (*s == '.') { *s = '\0'; components++; s++; } else if (*s == '"') { if (*(s+1) == '"') { /* escaped quote */ s += 2; } else { /* skip quoted string */ s++; while ((*s != '"') && (*s != '\0')) s++; if (*s == '"') { s++; } } } else { s++; } } pieces = calloc(components+1, sizeof (char *)); if (!pieces) { free(data); return (NULL); } /* store in pieces in inverted order */ for (i = (components-1), s = data; i > -1; i--) { *(pieces+i) = s; while (*s != '\0') s++; s++; } *(pieces+components) = NULL; *levels = components; return (pieces); } void __free_break_name(char **components, int levels) { free(components[levels-1]); free(components); } int __name_distance( char **targ, /* The target name */ char **test) /* the test name */ { int distance = 0; /* Don't count common components */ while ((*targ && *test) && (strcasecmp(*targ, *test) == 0)) { targ++; test++; } /* count off the legs of each name */ while (*test != NULL) { test++; distance++; } while (*targ != NULL) { targ++; distance++; } return (distance); } int __dir_same(char **test, char **targ) { /* skip common components */ while ((*targ && *test) && (strcasecmp(*targ, *test) == 0)) { targ++; test++; } return (*test == NULL && *targ == NULL); } void __broken_name_print(char **name, int levels) { int i; for (i = levels-1; i >= 0; --i) (void) printf("%s.", name[i]); } /* * For returning errors in a NIS result structure */ nis_result * nis_make_error( nis_error err, uint32_t aticks, /* Profile information for client */ uint32_t cticks, uint32_t dticks, uint32_t zticks) { nis_result *nres; nres = malloc(sizeof (nis_result)); if (!nres) return ((nis_result *)&__nomem_nis_result); (void) memset(nres, 0, sizeof (nis_result)); nres->status = err; nres->aticks = aticks; nres->zticks = zticks; nres->dticks = dticks; nres->cticks = cticks; return (nres); } #define ZVAL zattr_val.zattr_val_val #define ZLEN zattr_val.zattr_val_len /* * __cvt2attr() * * This function converts a search criteria of the form : * [ =, =, ... ] * Into an array of nis_attr structures. */ nis_attr * __cvt2attr( int *na, /* Number of attributes */ char **attrs) /* Strings associated with them */ { int i; nis_attr *zattrs; char *s; zattrs = calloc(*na, sizeof (nis_attr)); if (!zattrs) return (NULL); for (i = 0; i < *na; i++) { zattrs[i].zattr_ndx = *(attrs+i); for (s = zattrs[i].zattr_ndx; *s != '\0'; s++) { if (*s == '=') { *s = '\0'; s++; zattrs[i].ZVAL = s; zattrs[i].ZLEN = (uint_t)strlen(s) + 1; break; } else if (*s == '"') { /* advance s to matching quote */ s++; while ((*s != '"') && (*s != '\0')) s++; if (*s == '\0') { /* unterminated quote */ free(zattrs); return (NULL); } } } /* * POLICY : Missing value for an index name is an * error. The other alternative is the missing * value means "is present" unfortunately there * is no standard "is present" indicator in the * existing databases. * ANSWER : Always return an error. */ if (!zattrs[i].ZVAL) { free(zattrs); return (NULL); } } return (zattrs); } /* * nis_free_request() * * Free memory associated with a constructed list request. */ void nis_free_request(ib_request *req) { if (req->ibr_srch.ibr_srch_len) { /* free the string memory */ free(req->ibr_srch.ibr_srch_val[0].zattr_ndx); /* free the nis_attr array */ free(req->ibr_srch.ibr_srch_val); } if (req->ibr_name) free(req->ibr_name); } /* * nis_get_request() * * This function takes a NIS name, and converts it into an ib_request * structure. The request can then be used in a call to the nis service * functions. If the name wasn't parseable it returns an appropriate * error. This function ends up allocating an array of nis_attr structures * and a duplicate of the name string passed. To free this memory you * can call nis_free_request(), or you can simply free the first nis_attr * zattr_ndx pointer (the string memory) and the nis_attr pointer which * is the array. */ nis_error nis_get_request( nis_name name, /* search criteria + Table name */ nis_object *obj, /* Object for (rem/modify/add) */ netobj *cookie, /* Pointer to a cookie */ ib_request *req) /* Request structure to fill in */ { char *s, *t; /* Some string pointer temps */ char *p; /* temp var */ char **attr; /* Intermediate attributes */ int i; /* Counter variable */ char *data; /* pointer to malloc'd string */ int zn = 0; /* Count of attributes */ size_t datalen; /* length of malloc'd data */ char namebuf[NIS_MAXNAMELEN]; uchar_t within_attr_val; /* * a boolean to indicate the current parse * location is within the attribute value * - so that we can stop deleting white * space within an attribute value */ (void) memset(req, 0, sizeof (ib_request)); /* * if we're passed an object but no name, use the name from * the object instead. */ if (obj && !name) { if ((strlen(obj->zo_name)+strlen(obj->zo_domain)+2) > sizeof (namebuf)) { return (NIS_BADNAME); } (void) snprintf(namebuf, sizeof (namebuf), "%s.%s", obj->zo_name, obj->zo_domain); name = namebuf; } if (!name || (name[0] == '\0')) return (NIS_BADNAME); s = name; /* Move to the start of the components */ while (isspace(*s)) s++; if (*s == '[') { s++; /* Point past the opening bracket */ datalen = strlen(s); data = calloc(1, datalen+1); if (!data) return (NIS_NOMEMORY); t = data; /* Point to the databuffer */ while ((*s != '\0') && (*s != ']')) { while (isspace(*s)) { s++; } /* Check to see if we finished off the string */ if ((*s == '\0') || (*s == ']')) break; /* If *s == comma its a null criteria */ if (*s == COMMA) { s++; continue; } /* Not a space and not a comma, process an attr */ zn++; within_attr_val = 0; /* not within attr_val right now */ while ((*s != COMMA) && (*s != ']') && (*s != '\0')) { if (*s == '"') { if (*(s+1) == '"') { /* escaped quote */ *t++ = *s; /* copy one quote */ s += 2; } else { /* skip quoted string */ s++; while ((*s != '"') && (*s != '\0')) *t++ = *s++; if (*s == '"') { s++; } } } else if (*s == '=') { *t++ = *s++; within_attr_val = 1; } else if (isspace(*s) && !within_attr_val) { s++; } else *t++ = *s++; } *t++ = '\0'; /* terminate the attribute */ if (*s == COMMA) s++; } if (*s == '\0') { free(data); return (NIS_BADATTRIBUTE); } /* It wasn't a '\0' so it must be the closing bracket. */ s++; /* Skip any intervening white space and "comma" */ while (isspace(*s) || (*s == COMMA)) { s++; } /* Copy the name into our malloc'd buffer */ (void) strcpy(t, s); /* * If we found any attributes we process them, the * data string at this point is completely nulled * out except for attribute data. We recover this * data by scanning the string (we know how long it * is) and saving to each chunk of non-null data. */ if (zn) { /* Save this as the table name */ req->ibr_name = strdup(t); attr = calloc(zn+1, sizeof (char *)); if (!attr) { free(data); free(req->ibr_name); req->ibr_name = 0; return (NIS_NOMEMORY); } /* store in pieces in attr array */ for (i = 0, s = data; i < zn; i++) { *(attr+i) = s; /* Advance s past this component */ while (*s != '\0') s++; s++; } *(attr+zn) = NULL; } else { free(data); req->ibr_name = strdup(s); } } else { /* Null search criteria */ req->ibr_name = strdup(s); data = NULL; } if (zn) { req->ibr_srch.ibr_srch_len = zn; req->ibr_srch.ibr_srch_val = __cvt2attr(&zn, attr); free(attr); /* don't need this any more */ if (!(req->ibr_srch.ibr_srch_val)) { req->ibr_srch.ibr_srch_len = 0; free(req->ibr_name); req->ibr_name = 0; free(data); return (NIS_BADATTRIBUTE); } } /* check for correct quotes in ibr_name (but leave them in) */ for (p = req->ibr_name; *p; p++) { if (*p == '"') { /* advance p to the matching quote */ p++; while (*p != '"' && *p != '\0') { p++; } if (*p == '\0') { req->ibr_srch.ibr_srch_len = 0; free(req->ibr_name); req->ibr_name = 0; free(data); return (NIS_BADNAME); } } } if (obj) { req->ibr_obj.ibr_obj_len = 1; req->ibr_obj.ibr_obj_val = obj; } if (cookie) { req->ibr_cookie = *cookie; } return (NIS_SUCCESS); } /* Various subroutines used by the server code */ nis_object * nis_read_obj(char *f) /* name of the object to read */ { FILE *rootfile; int status; /* Status of the XDR decoding */ XDR xdrs; /* An xdr stream handle */ nis_object *res; res = calloc(1, sizeof (nis_object)); if (!res) return (NULL); rootfile = fopen(f, "r"); if (rootfile == NULL) { /* This is ok if we are the root of roots. */ free(res); return (NULL); } /* Now read in the object */ xdrstdio_create(&xdrs, rootfile, XDR_DECODE); status = xdr_nis_object(&xdrs, res); xdr_destroy(&xdrs); (void) fclose(rootfile); if (!status) { syslog(LOG_ERR, "Object file %s is corrupt!", f); xdr_free(xdr_nis_object, (char *)res); free(res); return (NULL); } return (res); } int nis_write_obj( char *f, /* name of the object to read */ nis_object *o) /* The object to write */ { FILE *rootfile; int status; /* Status of the XDR decoding */ XDR xdrs; /* An xdr stream handle */ rootfile = fopen(f, "w"); if (rootfile == NULL) { return (0); } /* Now encode the object */ xdrstdio_create(&xdrs, rootfile, XDR_ENCODE); status = xdr_nis_object(&xdrs, o); xdr_destroy(&xdrs); (void) fclose(rootfile); return (status); } /* * nis_make_rpchandle() * * This is a generic version of clnt_creat() for NIS. It localizes * _all_ of the changes needed to port to TLI RPC into this one * section of code. */ /* * Transport INDEPENDENT RPC code. This code assumes you * are using the new RPC/tli code and will build * a ping handle on top of a datagram transport. */ /* * __map_addr() * * This is our internal function that replaces rpcb_getaddr(). We * build our own to prevent calling netdir_getbyname() which could * recurse to the nameservice. */ static char * __map_addr( struct netconfig *nc, /* Our transport */ char *uaddr, /* RPCBIND address */ rpcprog_t prog, /* Name service Prog */ rpcvers_t ver) { CLIENT *client; RPCB parms; /* Parameters for RPC binder */ enum clnt_stat clnt_st; /* Result from the rpc call */ char *ua = NULL; /* Universal address of service */ char *res = NULL; /* Our result to the parent */ struct timeval tv; /* Timeout for our rpcb call */ int ilen, olen; /* buffer length for clnt_tli_create */ /* * If using "udp", use __nisipbufsize if inbuf and outbuf are set to 0. */ if (strcmp(NC_UDP, nc->nc_proto) == 0) { /* for udp only */ ilen = olen = __nisipbufsize; } else { ilen = olen = 0; } client = __nis_clnt_create(RPC_ANYFD, nc, uaddr, 0, 0, RPCBPROG, RPCBVERS, ilen, olen); if (!client) return (NULL); (void) clnt_control(client, CLSET_FD_CLOSE, NULL); /* * Now make the call to get the NIS service address. * We set the retry timeout to 3 seconds so that we * will retry a few times. Retries should be rare * because we are usually only called when we know * a server is available. */ tv.tv_sec = 3; tv.tv_usec = 0; (void) clnt_control(client, CLSET_RETRY_TIMEOUT, (char *)&tv); tv.tv_sec = 10; tv.tv_usec = 0; parms.r_prog = prog; parms.r_vers = ver; parms.r_netid = nc->nc_netid; /* not needed */ parms.r_addr = ""; /* not needed; just for xdring */ parms.r_owner = ""; /* not needed; just for xdring */ clnt_st = clnt_call(client, RPCBPROC_GETADDR, xdr_rpcb, (char *)&parms, xdr_wrapstring, (char *)&ua, tv); if (clnt_st == RPC_SUCCESS) { clnt_destroy(client); if (*ua == '\0') { free(ua); return (NULL); } res = strdup(ua); xdr_free(xdr_wrapstring, (char *)&ua); return (res); } else if (((clnt_st == RPC_PROGVERSMISMATCH) || (clnt_st == RPC_PROGUNAVAIL)) && (strcmp(nc->nc_protofmly, NC_INET) == 0)) { /* * version 3 not available. Try version 2 * The assumption here is that the netbuf * is arranged in the sockaddr_in * style for IP cases. * * Note: If the remote host doesn't support version 3, * we assume it doesn't know IPv6 either. */ ushort_t port; struct sockaddr_in *sa; struct netbuf remote; int protocol; char buf[32]; (void) clnt_control(client, CLGET_SVC_ADDR, (char *)&remote); /* LINTED pointer cast */ sa = (struct sockaddr_in *)(remote.buf); protocol = strcmp(nc->nc_proto, NC_TCP) ? IPPROTO_UDP : IPPROTO_TCP; port = (ushort_t)pmap_getport(sa, prog, ver, protocol); if (port != 0) { port = htons(port); (void) sprintf(buf, "%d.%d.%d.%d.%d.%d", (sa->sin_addr.s_addr >> 24) & 0xff, (sa->sin_addr.s_addr >> 16) & 0xff, (sa->sin_addr.s_addr >> 8) & 0xff, (sa->sin_addr.s_addr) & 0xff, (port >> 8) & 0xff, port & 0xff); res = strdup(buf); } else res = NULL; clnt_destroy(client); return (res); } if (clnt_st == RPC_TIMEDOUT) syslog(LOG_ERR, "NIS+ server not responding"); else syslog(LOG_ERR, "NIS+ server could not be contacted: %s", clnt_sperrno(clnt_st)); clnt_destroy(client); return (NULL); } char * __nis_get_server_address(struct netconfig *nc, endpoint *ep) { return (__map_addr(nc, ep->uaddr, NIS_PROG, NIS_VERSION)); } #define MAX_EP (20) int __nis_get_callback_addresses(endpoint *ep, endpoint **ret_eps) { int i; int n; int st; int nep = 0; endpoint *eps; struct nd_hostserv hs; struct nd_addrlist *addrs; struct nd_mergearg ma; void *lh; void *nch; struct netconfig *nc; eps = malloc(MAX_EP * sizeof (endpoint)); if (eps == 0) return (0); hs.h_host = HOST_SELF; hs.h_serv = "rpcbind"; /* as good as any */ lh = __inet_get_local_interfaces(); nch = setnetconfig(); while ((nc = getnetconfig(nch)) != NULL) { if (strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0) continue; if (nc->nc_semantics != NC_TPI_COTS && nc->nc_semantics != NC_TPI_COTS_ORD) continue; st = netdir_getbyname(nc, &hs, &addrs); if (st != 0) continue; /* * The netdir_merge code does not work very well * for inet if the client and server are not * on the same network. Instead, we try each local * address. * * For other protocol families and for servers on a * local network, we use the regular merge code. */ if (strcmp(nc->nc_protofmly, NC_INET) == 0 && !__inet_uaddr_is_local(lh, nc, ep->uaddr)) { n = __inet_address_count(lh); for (i = 0; i < n; i++) { if (nep >= MAX_EP) { syslog(LOG_INFO, "__nis_get_callback_addresses: too many endpoints"); goto full; } eps[nep].uaddr = __inet_get_uaddr(lh, nc, i); if (eps[nep].uaddr == 0) continue; if (strcmp(eps[nep].uaddr, LOOP_UADDR) == 0) { free(eps[nep].uaddr); continue; } __nis_netconfig2ep(nc, &(eps[nep])); nep++; } } else { ma.s_uaddr = ep->uaddr; ma.c_uaddr = taddr2uaddr(nc, addrs->n_addrs); ma.m_uaddr = 0; (void) netdir_options(nc, ND_MERGEADDR, 0, (void *)&ma); free(ma.s_uaddr); free(ma.c_uaddr); if (nep >= MAX_EP) { syslog(LOG_INFO, "__nis_get_callback_addresses: too many endpoints"); goto full; } eps[nep].uaddr = ma.m_uaddr; __nis_netconfig2ep(nc, &(eps[nep])); nep++; } netdir_free((void *)addrs, ND_ADDRLIST); } full: (void) endnetconfig(nch); __inet_free_local_interfaces(lh); *ret_eps = eps; return (nep); } /* * Try to create a RPC GSS security context (flavor RPCSEC_GSS). * Returns auth handle on success, else NULL. Set flag 'try_auth_des' * to TRUE if the AUTH_DES compat line is found in the security conf file * or no valid mech entries are found in the conf file. */ static AUTH * create_rpcgss_secctx( CLIENT *clnt, /* out */ nis_server *srv, char *gss_svc, bool_t *try_auth_des) /* out */ { mechanism_t **mechs; /* list of mechanisms */ *try_auth_des = FALSE; if (mechs = __nis_get_mechanisms(TRUE)) { mechanism_t **mpp; char svc_name[NIS_MAXNAMELEN+1] = {0}; /* Check RPC GSS service name buf size. */ if ((strlen(gss_svc ? gss_svc : NIS_SVCNAME_NISD) + 1 + strlen(srv->name) + 1) > sizeof (svc_name)) { syslog(LOG_ERR, "nis_make_rpchandle_gss_svc: RPC GSS service name too long"); __nis_release_mechanisms(mechs); return (NULL); } /* RPC GSS service names are of the form svc@host.dom */ (void) snprintf(svc_name, sizeof (svc_name), "%s@%s", gss_svc ? gss_svc : NIS_SVCNAME_NISD, srv->name); /* * Loop thru all the available mech entries until an * RPC GSS security context is established or until * the AUTH_DES compat entry is found. */ for (mpp = mechs; *mpp; mpp++) { mechanism_t *mp = *mpp; if (AUTH_DES_COMPAT_CHK(mp)) { __nis_release_mechanisms(mechs); *try_auth_des = TRUE; return (NULL); } if (!VALID_MECH_ENTRY(mp)) { syslog(LOG_ERR, "%s: invalid mechanism entry name '%s'", NIS_SEC_CF_PATHNAME, mp->mechname ? mp->mechname : "NULL"); continue; } /* * If the mechanism is of the public key crypto * technology variety, let's make sure the server's * public key exists and the clients secret key is set * before going thru the expense of a RPC GSS security * context creation attempt. */ if (MECH_PK_TECH(mp) && ((srv->key_type == NIS_PK_DHEXT && !__nis_dhext_extract_pkey(&(srv->pkey), mp->keylen, mp->algtype)) || !key_secretkey_is_set_g(mp->keylen, mp->algtype))) { #ifdef DHEXT_DEBUG (void) fprintf(stderr, "nis_make_rpchandle_gss_svc: srv keytype = %d: No keys, skip mech '%s' ...\n", srv->key_type, mp->alias); #endif continue; } clnt->cl_auth = rpc_gss_seccreate(clnt, svc_name, mp->mechname, mp->secserv, mp->qop, NULL, NULL); if (clnt->cl_auth) { __nis_release_mechanisms(mechs); return (clnt->cl_auth); /* we're in bizness */ #ifdef DHEXT_DEBUG } else { rpc_gss_error_t err; rpc_gss_get_error(&err); (void) fprintf(stderr, "nis_make_rpchandle_gss_svc: RPCGSS_SecCreat fail: gerr = %d serr = %d\n", err.rpc_gss_error, err.system_error); #endif /* DHEXT_DEBUG */ } } __nis_release_mechanisms(mechs); } else { /* no valid mechs, fallback to AUTH_DES */ *try_auth_des = TRUE; } return (NULL); } CLIENT * nis_make_rpchandle( nis_server *srv, /* NIS Server description */ int cback, /* Boolean indicating callback address */ rpcprog_t prog, /* Program number */ rpcvers_t ver, /* Version */ uint_t flags, /* Flags, {VC, DG, AUTH} */ int inbuf, /* Preferred buffer sizes */ int outbuf) /* for input and output */ { return (nis_make_rpchandle_uaddr(srv, cback, prog, ver, flags, inbuf, outbuf, 0)); } CLIENT * nis_make_rpchandle_uaddr( nis_server *srv, /* NIS Server description */ int cback, /* Boolean indicating callback address */ rpcprog_t prog, /* Program number */ rpcvers_t ver, /* Version */ uint_t flags, /* Flags, {VC, DG, AUTH} */ int inbuf, /* Preferred buffer sizes */ int outbuf, /* for input and output */ char *uaddr) /* optional address of server */ { return (nis_make_rpchandle_gss_svc(srv, cback, prog, ver, flags, inbuf, outbuf, uaddr, NULL)); } extern int __can_use_af(sa_family_t af); CLIENT * __nis_clnt_create(int fd, struct netconfig *nc, char *uaddr, struct netbuf *addr, int domapaddr, int prog, int ver, int inbuf, int outbuf) { char *svc_addr; CLIENT *clnt; int freeaddr = 0; /* Sanity check */ if (nc == 0 || (addr == 0 && uaddr == 0)) { return (0); } /* * Check if we have a useable interface for this address family. * This check properly belongs in RPC (or even further down), * but until they provide it, we roll our own. */ if (__can_use_af((strcmp(nc->nc_protofmly, NC_INET6) == 0) ? AF_INET6 : AF_INET) == 0) { return (0); } if (domapaddr) { svc_addr = __map_addr(nc, uaddr, prog, ver); if (svc_addr == 0) return (0); addr = uaddr2taddr(nc, svc_addr); freeaddr = 1; free(svc_addr); } else if (addr == 0) { addr = uaddr2taddr(nc, uaddr); freeaddr = 1; } if (addr == 0) { return (0); } clnt = clnt_tli_create(fd, nc, addr, prog, ver, outbuf, inbuf); if (clnt) { if (clnt_control(clnt, CLGET_FD, (char *)&fd)) /* make it "close on exec" */ (void) fcntl(fd, F_SETFD, FD_CLOEXEC); (void) clnt_control(clnt, CLSET_FD_CLOSE, NULL); } if (freeaddr) netdir_free(addr, ND_ADDR); return (clnt); } typedef struct { endpoint *ep; struct netconfig *nc; } alt_ep_t; /* * Construct an rpc handle. * * If the gss_svc arg is NULL, then default to "nisd" (rpc.nisd). */ static CLIENT * nis_make_rpchandle_gss_svc_ext( nis_server *srv, /* NIS Server description */ int cback, /* Boolean indicating callback address */ rpcprog_t prog, /* Program number */ rpcvers_t ver, /* Version */ uint_t flags, /* Flags, {VC, DG, AUTH} */ int inbuf, /* Preferred buffer sizes */ int outbuf, /* for input and output */ char *uaddr, /* optional address of server */ char *gss_svc, /* RPC GSS service name */ int use_realid) /* 1: Use REAL id, 0: use Eff. ids */ { CLIENT *clnt = 0; /* Client handle */ void *nc_handle; /* Netconfig "state" */ struct netconfig *nc; /* Various handles */ endpoint *ep; /* useful endpoints */ int epl, i; /* counters */ int uid, gid; /* Effective uid/gid */ char netname[MAXNETNAMELEN+1]; /* our netname */ char *hexkey = NULL; /* hex public key for DHEXT */ netobj xpkey = { NULL, 0}; bool_t try_auth_des; alt_ep_t *altep = 0; nc_handle = (void *) setnetconfig(); if (!nc_handle) return (NULL); ep = srv->ep.ep_val; epl = srv->ep.ep_len; if (uaddr) { char *fmly = (strchr(uaddr, ':') == 0) ? NC_INET : NC_INET6; while ((nc = getnetconfig(nc_handle)) != NULL) { /* Is it a visible transport ? */ if ((nc->nc_flag & NC_VISIBLE) == 0) continue; /* Does the protocol family match the uaddr ? */ if (strcmp(nc->nc_protofmly, fmly) != 0) continue; for (i = 0; i < epl; i++) { if (__nis_netconfig_matches_ep(nc, &ep[i])) { break; } } /* Did we find a matching endpoint ? */ if (i < epl) break; } if (nc == 0) { syslog(LOG_ERR, "nis_make_rpchandle: can't find netconfig entry for %s, %s", uaddr, fmly); return (0); } clnt = __nis_clnt_create(RPC_ANYFD, nc, uaddr, 0, 0, prog, ver, inbuf, outbuf); } else { altep = calloc(epl, sizeof (*altep)); /* * The transport policies : * Selected transport must be visible. * Must have requested or better semantics. * Must be correct protocol. */ while ((nc = getnetconfig(nc_handle)) != 0) { /* Is it a visible transport ? */ if ((nc->nc_flag & NC_VISIBLE) == 0) continue; /* If we asked for a virtual circuit, is it ? */ if (((flags & ZMH_VC) != 0) && (nc->nc_semantics != NC_TPI_COTS) && (nc->nc_semantics != NC_TPI_COTS_ORD)) continue; /* Check to see is we talk this protofmly, protocol */ for (i = 0; i < epl; i++) { if (__nis_netconfig_matches_ep(nc, &(ep[i]))) break; } /* Was it one of our transports ? */ if (i == epl) continue; /* No */ /* * If it is one of our supported transports, but isn't * a datagram and we want a datagram, keep looking but * remember this one as a possibility. */ if (((flags & ZMH_DG) != 0) && (nc->nc_semantics != NC_TPI_CLTS) && altep != 0) { altep[i].nc = nc; altep[i].ep = &ep[i]; /* This endpoint */ continue; } /* We've got a candidate; see if it works */ clnt = __nis_clnt_create(RPC_ANYFD, nc, ep[i].uaddr, 0, (cback == 0), prog, ver, inbuf, outbuf); if (clnt != 0) break; } if (altep != 0 && (!(flags & ZMH_NOFALLBACK))) { /* If primary choices failed, try the alternates */ for (i = 0; clnt == 0 && i < epl; i++) { if (altep[i].ep == 0) continue; clnt = __nis_clnt_create(RPC_ANYFD, altep[i].nc, altep[i].ep->uaddr, 0, (cback == 0), prog, ver, inbuf, outbuf); } free(altep); } } /* Done with the netconfig handle regardless */ (void) endnetconfig(nc_handle); /* If we still don't have a client handle, we're sunk */ if (clnt == 0) { return (0); } /* * No auth requested or it's a callback (which is not authenticated), * so we're done. */ if (!(flags & ZMH_AUTH) || cback) return (clnt); /* * Setup authentication. Try the RPCSEC_GSS flavor first, then * fallback to AUTH_DES (if requested) and, if need be, AUTH_SYS. */ if (create_rpcgss_secctx(clnt, srv, gss_svc, &try_auth_des)) return (clnt); if (!try_auth_des) /* XXXX what's the meaning of going into a switch stmt??? */ goto auth_sys; switch (srv->key_type) { case NIS_PK_DHEXT : /* * We're doing AUTH_DES, but the server might * have multiple keys so let's get the 192-0 one. */ if ((hexkey = __nis_dhext_extract_pkey(&(srv->pkey), 192, 0)) == NULL) goto auth_sys; xpkey.n_len = strlen(hexkey) + 1; xpkey.n_bytes = hexkey; /*FALLTHROUGH*/ case NIS_PK_DH : (void) host2netname(netname, srv->name, NULL); clnt->cl_auth = (AUTH *)authdes_pk_seccreate(netname, xpkey.n_len ? &xpkey : &(srv->pkey), 15, NULL, NULL, srv); if (xpkey.n_len) free(xpkey.n_bytes); if (clnt->cl_auth) break; /*FALLTHROUGH*/ case NIS_PK_NONE : auth_sys: uid = use_realid ? getuid() : geteuid(); gid = use_realid ? getgid() : getegid(); clnt->cl_auth = authsys_create(nis_local_host(), uid, gid, 0, NULL); if (clnt->cl_auth) break; /*FALLTHROUGH*/ default : clnt->cl_auth = authnone_create(); if (clnt->cl_auth) break; syslog(LOG_CRIT, "nis_make_rpchandle_uaddr: cannot create cred."); abort(); break; } if (clnt->cl_auth) return (clnt); clnt_destroy(clnt); return (NULL); } CLIENT * nis_make_rpchandle_gss_svc(nis_server *srv, int cback, rpcprog_t prog, rpcvers_t ver, uint_t flags, int inbuf, int outbuf, char *uaddr, char *gss_svc) { return (nis_make_rpchandle_gss_svc_ext(srv, cback, prog, ver, flags, inbuf, outbuf, uaddr, gss_svc, 0)); } CLIENT * nis_make_rpchandle_gss_svc_ruid(nis_server *srv, int cback, rpcprog_t prog, rpcvers_t ver, uint_t flags, int inbuf, int outbuf, char *uaddr, char *gss_svc) { return (nis_make_rpchandle_gss_svc_ext(srv, cback, prog, ver, flags, inbuf, outbuf, uaddr, gss_svc, 1)); } static mutex_t __nis_ss_used_lock = DEFAULTMUTEX; /* lock level 3 */ int __nis_ss_used = 0; /* * nis_get_static_storage() * * This function is used by various functions in their effort to minimize the * hassles of memory management in an RPC daemon. Because the service doesn't * implement any hard limits, this function allows people to get automatically * growing buffers that meet their storage requirements. It returns the * pointer in the nis_sdata structure. * */ void * nis_get_static_storage( struct nis_sdata *bs, /* User buffer structure */ uint_t el, /* Sizeof elements */ uint_t nel) /* Number of elements */ { uint_t sz; sz = nel * el; if (!bs) return (NULL); if (!bs->buf) { bs->buf = malloc(sz); if (!bs->buf) return (NULL); bs->size = sz; sig_mutex_lock(&__nis_ss_used_lock); __nis_ss_used += sz; sig_mutex_unlock(&__nis_ss_used_lock); } else if (bs->size < sz) { int size_delta; free(bs->buf); size_delta = - (bs->size); bs->buf = malloc(sz); /* check the result of malloc() first */ /* then update the statistic. */ if (!bs->buf) return (NULL); bs->size = sz; size_delta += sz; sig_mutex_lock(&__nis_ss_used_lock); __nis_ss_used += size_delta; sig_mutex_unlock(&__nis_ss_used_lock); } (void) memset(bs->buf, 0, sz); /* SYSV version of bzero() */ return (bs->buf); } char * nis_old_data_r( char *s, struct nis_sdata *bs_ptr) { char *buf; char temp[1024]; size_t len = 0; buf = (char *)nis_get_static_storage(bs_ptr, 1, 1024); if (!buf) return (NULL); /* * this saving of 's' is because the routines that call nis_data() * are not very careful about what they pass in. Sometimes what they * pass in are 'static' returned from some of the routines called * below nis_leaf_of(), nis_local_host() and so on. */ if (s) { len = strlen(s) + 1; if (len >= sizeof (temp)) return (NULL); (void) snprintf(temp, sizeof (temp), "/%s", s); } if (len + strlen(__nis_data_directory) + strlen(nis_leaf_of(nis_local_host())) >= bs_ptr->size) return (NULL); (void) strcpy(buf, __nis_data_directory); (void) strcat(buf, nis_leaf_of(nis_local_host())); if (s) (void) strcat(buf, temp); for (s = buf; *s; s++) { if (isupper(*s)) *s = tolower(*s); } return (buf); } char * nis_old_data(char *s) { static pthread_key_t bs_key; static struct nis_sdata bs_main; struct nis_sdata *bs_ptr; bs_ptr = thr_main()? &bs_main : thr_get_storage(&bs_key, sizeof (struct nis_sdata), destroy_nis_sdata); return (nis_old_data_r(s, bs_ptr)); } char * nis_data_r(char *s, struct nis_sdata *bs_ptr) { char *buf; char temp[1024]; size_t len = 0; buf = (char *)nis_get_static_storage(bs_ptr, 1, 1024); if (!buf) return (NULL); /* * this saving of 's' is because the routines that call nis_data() * are not very careful about what they pass in. Sometimes what they * pass in are 'static' returned from some of the routines called * below nis_leaf_of(), nis_local_host() and so on. */ if (s) { len = strlen(s) + 1; if (len >= sizeof (temp)) return (NULL); (void) snprintf(temp, sizeof (temp), "/%s", s); } if (len + strlen(__nis_data_directory) + strlen(NIS_DIR) >= bs_ptr->size) return (NULL); (void) strcpy(buf, __nis_data_directory); (void) strcat(buf, NIS_DIR); if (s) (void) strcat(buf, temp); for (s = buf; *s; s++) { if (isupper(*s)) *s = tolower(*s); } return (buf); } char * nis_data(char *s) { static pthread_key_t bs_key; static struct nis_sdata bs_main; struct nis_sdata *bs_ptr; bs_ptr = thr_main()? &bs_main : thr_get_storage(&bs_key, sizeof (struct nis_sdata), destroy_nis_sdata); return (nis_data_r(s, bs_ptr)); } /* * Return the directory name of the root_domain of the caller's NIS+ * domain. * * This routine is a temporary implementation and should be * provided as part of the the NIS+ project. See RFE: 1103216 * Required for root replication. * * XXX MT safing: local_root_lock protects the local_root structure. * * It tries to determine the root domain * name by "walking" the path up the NIS+ directory tree, starting * at nis_local_directory() until a NIS_NOSUCHNAME or NIS_NOTFOUND error * is obtained. Returns 0 on fatal errors obtained before this point, * or if it exhausts the domain name without ever obtaining one of * of these errors. */ static nis_name local_root = 0; static mutex_t local_root_lock = DEFAULTMUTEX; nis_name __nis_local_root(void) { char *dir; int found_root = 0; int try_count = 0; int fatal_error = 0; char *prev_testdir; char *testdir; sig_mutex_lock(&local_root_lock); if (local_root) { sig_mutex_unlock(&local_root_lock); return (local_root); } local_root = calloc(1, LN_BUFSIZE); if (!local_root) { sig_mutex_unlock(&local_root_lock); return (0); } /* walk up NIS+ tree till we find the root. */ dir = strdup(__nis_rpc_domain()); prev_testdir = dir; testdir = nis_domain_of(prev_testdir); while (testdir && !found_root && !fatal_error) { /* try lookup */ nis_result* nis_ret = nis_lookup(testdir, 0); /* handle return status */ switch (nis_ret->status) { case NIS_SUCCESS: case NIS_S_SUCCESS: try_count = 0; prev_testdir = testdir; testdir = nis_domain_of(prev_testdir); break; case NIS_NOSUCHNAME: case NIS_NOTFOUND: case NIS_NOT_ME: case NIS_FOREIGNNS: found_root = 1; break; case NIS_TRYAGAIN: case NIS_CACHEEXPIRED: /* sleep 1 second and try same name again, up to 10 times */ /* REMIND: This is arbitrary! BAD! */ (void) sleep(1); fatal_error = (try_count++ > 9); break; case NIS_NAMEUNREACHABLE: case NIS_SYSTEMERROR: case NIS_RPCERROR: case NIS_NOMEMORY: default: fatal_error = 1; break; } if (nis_ret) nis_freeresult(nis_ret); } if (!found_root) { free(dir); sig_mutex_unlock(&local_root_lock); return (0); } (void) strcpy(local_root, prev_testdir); free(dir); sig_mutex_unlock(&local_root_lock); return (local_root); } extern void __pkey_cache_add(char *, char *, keylen_t, algtype_t); extern int bin2hex(int, unsigned char *, char *); /* * __nis_cache_server_pkeys * * Add the public keys for the servers of the directory object to the * per-process public key cache. */ void __nis_cache_server_pkeys(directory_obj *dir) { int i; nis_server *srv; char netname[MAXNETNAMELEN+1]; char pkey[MAX_NETOBJ_SZ+1]; extdhkey_t *key; uint_t s; if (dir == NULL) return; for (i = 0; i < dir->do_servers.do_servers_len; i++) { srv = &(dir->do_servers.do_servers_val[i]); switch (srv->key_type) { case NIS_PK_DH: if (srv->pkey.n_len < sizeof (pkey) && host2netname(netname, srv->name, NULL)) { (void) memcpy(pkey, srv->pkey.n_bytes, srv->pkey.n_len); pkey[srv->pkey.n_len] = '\0'; __pkey_cache_add(netname, pkey, 192, 0); } break; case NIS_PK_DHEXT: if (!host2netname(netname, srv->name, NULL)) break; for (s = 0; s < srv->pkey.n_len; ) { keylen_t k, kpadlen; algtype_t a; /* LINTED pointer cast */ key = (extdhkey_t *)&(srv->pkey.n_bytes[s]); k = ntohs(key->keylen); if (k == 0) break; kpadlen = ((((k+7)/8)+3)/4)*4; a = ntohs(key->algtype); if (kpadlen <= sizeof (pkey)) { (void) bin2hex((k+7)/8, key->key, pkey); __pkey_cache_add(netname, pkey, k, a); } s += 2*sizeof (ushort_t) + kpadlen; } break; default: break; } } }