#include "mt.h"
#include <pwd.h>
#include <grp.h>
#include <syslog.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/fcntl.h>
#include <netinet/in.h>
#include <rpc/rpc.h>
#include <rpc/svc.h>
#include <tiuser.h>
#include <netconfig.h>
#include <netdir.h>
#include <rpc/rpcb_clnt.h>
#include <rpc/pmap_clnt.h>
#include <rpcsvc/nis.h>
#include <rpcsvc/nis_dhext.h>
#include "nis_clnt.h"
#include <sys/systeminfo.h>
#include <nsswitch.h>
#define MAXIPRINT (11)
int __nisipbufsize = 8192;
static struct local_names *__get_local_names(void);
static char *__map_addr(struct netconfig *, char *, rpcprog_t, rpcvers_t);
name_pos
nis_dir_cmp(
nis_name n1,
nis_name n2)
{
size_t l1, l2;
name_pos result;
if ((n1 == NULL) || (n2 == NULL))
return (BAD_NAME);
l1 = strlen(n1);
l2 = strlen(n2);
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 {
nis_name ntmp;
size_t ltmp;
ntmp = n1; n1 = n2; n2 = ntmp;
ltmp = l1; l1 = l2; l2 = ltmp;
result = HIGHER_NAME;
}
if (l2 == 0) {
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;
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 struct local_names *
__get_local_names1(void)
{
char *t;
if (ln != NULL) {
return (ln);
}
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 (ln->domain[strlen(ln->domain)-1] != '.')
(void) strcat(ln->domain, ".");
if (sysinfo(SI_HOSTNAME, ln->host, LN_BUFSIZE) < 0)
return (ln);
t = strchr(ln->host, '.');
if (t)
*t = 0;
if (ln->domain[0] != '.')
(void) strcat(ln->host, ".");
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;
char *temp;
(void) strncpy(ln->group, t, maxlen);
if (strcmp(ln->group, "") == 0) {
return (ln);
}
ln->group[maxlen] = '\0';
temp = strrchr(ln->group, '.');
if ((temp == NULL) || (temp[1] != '\0')) {
ln->group[maxlen - (strlen(ln->domain)+1)] = '\0';
if (ln->domain[0] != '.') {
(void) strcat(ln->group, ".");
}
(void) strcat(ln->group, ln->domain);
}
}
return (ln);
}
nis_name
nis_local_group(void)
{
struct local_names *ln = __get_local_names();
if (!ln)
return (NULL);
return (ln->group);
}
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_name
nis_domain_of(char *s)
{
char *d;
d = __nis_nextsep_of(s);
if (d == NULL)
return (NULL);
if (*d == '.')
d++;
if (*d == '\0')
return (".");
return (d);
}
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 = PTHREAD_ONCE_KEY_NP;
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
nis_name_of_r(
char *s,
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
if ((!s) || (!ln))
return (NULL);
d = &(ln->domain[0]);
dl = strlen(ln->domain);
sl = strlen(s);
if (sl >= bufsize || (s[sl-1] != '.' && sl >= bufsize-1))
return (NULL);
(void) strcpy(buf, s);
if (buf[sl-1] != '.') {
(void) strcat(buf, ".");
sl++;
}
if (dl == 1) {
buf[sl-1] = '\0';
return (buf);
}
p = nis_dir_cmp(buf, d);
if ((p == HIGHER_NAME) || (p == NOT_SEQUENTIAL) || (p == SAME_NAME))
return (NULL);
buf[(sl - dl) - 1] = '\0';
if (buf[0] == '\0')
return (NULL);
return (buf);
}
nis_name
nis_name_of(
char *s)
{
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_name
nis_local_directory(void)
{
struct local_names *ln = __get_local_names();
if (ln == NULL)
return (NULL);
return (ln->domain);
}
nis_name
__nis_rpc_domain()
{
struct local_names *ln = __get_local_names();
if (ln == NULL)
return (NULL);
return (ln->rpcdomain);
}
nis_name
nis_local_host(void)
{
struct local_names *ln = __get_local_names();
if (ln == NULL)
return (NULL);
return (ln->host);
}
void
nis_destroy_object(nis_object *obj)
{
if (obj == 0)
return;
xdr_free(xdr_nis_object, (char *)obj);
free(obj);
}
static void
destroy_nis_sdata(void *p)
{
struct nis_sdata *ns = p;
if (ns->buf != 0)
free(ns->buf);
free(ns);
}
nis_object *
nis_clone_object_r(
nis_object *obj,
nis_object *dest,
struct nis_sdata *clone_buf_ptr)
{
nis_object *result;
int status;
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);
if (dest) {
(void) memset(dest, 0, sizeof (nis_object));
result = dest;
} else
result = calloc(1, sizeof (nis_object));
if (result == NULL)
return (NULL);
(void) xdr_setpos(&in_xdrs, 0);
status = xdr_nis_object(&in_xdrs, obj);
if (status == FALSE)
return (NULL);
(void) xdr_setpos(&out_xdrs, 0);
status = xdr_nis_object(&out_xdrs, result);
if (status == FALSE)
return (NULL);
return (result);
}
nis_object *
nis_clone_object(
nis_object *obj,
nis_object *dest)
{
static pthread_key_t clone_buf_key = PTHREAD_ONCE_KEY_NP;
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));
}
nis_object *
nis_read_obj(char *f)
{
FILE *rootfile;
int status;
XDR xdrs;
nis_object *res;
res = calloc(1, sizeof (nis_object));
if (!res)
return (NULL);
rootfile = fopen(f, "rF");
if (rootfile == NULL) {
free(res);
return (NULL);
}
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,
nis_object *o)
{
FILE *rootfile;
int status;
XDR xdrs;
rootfile = fopen(f, "wF");
if (rootfile == NULL) {
return (0);
}
xdrstdio_create(&xdrs, rootfile, XDR_ENCODE);
status = xdr_nis_object(&xdrs, o);
xdr_destroy(&xdrs);
(void) fclose(rootfile);
return (status);
}
static char *
__map_addr(
struct netconfig *nc,
char *uaddr,
rpcprog_t prog,
rpcvers_t ver)
{
CLIENT *client;
RPCB parms;
enum clnt_stat clnt_st;
char *ua = NULL;
char *res = NULL;
struct timeval tv;
int ilen, olen;
if (strcmp(NC_UDP, nc->nc_proto) == 0) {
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);
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;
parms.r_addr = "";
parms.r_owner = "";
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)) {
ushort_t port;
struct sockaddr_in *sa;
struct netbuf remote;
int protocol;
char buf[32];
(void) clnt_control(client, CLGET_SVC_ADDR, (char *)&remote);
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);
}
#define MAX_EP (20)
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;
if (nc == 0 || (addr == 0 && uaddr == 0)) {
return (0);
}
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))
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
(void) clnt_control(clnt, CLSET_FD_CLOSE, NULL);
}
if (freeaddr)
netdir_free(addr, ND_ADDR);
return (clnt);
}
static mutex_t __nis_ss_used_lock = DEFAULTMUTEX;
int __nis_ss_used = 0;
void *
nis_get_static_storage(
struct nis_sdata *bs,
uint_t el,
uint_t nel)
{
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);
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);
return (bs->buf);
}