1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
24 */
25
26/*
27 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
28 * Use is subject to license terms.
29 */
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <sys/types.h>
34#include <string.h>
35#include <syslog.h>
36#include <sys/param.h>
37#include <rpc/rpc.h>
38#include <sys/stat.h>
39#include <netconfig.h>
40#include <netdir.h>
41#include <sys/file.h>
42#include <sys/time.h>
43#include <sys/errno.h>
44#include <sys/resource.h>
45#include <rpcsvc/mount.h>
46#include <sys/pathconf.h>
47#include <sys/systeminfo.h>
48#include <sys/utsname.h>
49#include <signal.h>
50#include <locale.h>
51#include <unistd.h>
52#include <thread.h>
53#include <sharefs/share.h>
54#include "../lib/sharetab.h"
55#include "mountd.h"
56
57struct cache_entry {
58	char	*cache_host;
59	time_t	cache_time;
60	int	cache_belong;
61	char	**cache_grl;
62	int	cache_grc;
63	struct cache_entry *cache_next;
64};
65
66static struct cache_entry *cache_head;
67
68#define	VALID_TIME	60  /* seconds */
69
70static rwlock_t cache_lock;	/* protect the cache chain */
71
72static void cache_free(struct cache_entry *entry);
73static int cache_check(char *host, char **grl, int grc, int *belong);
74static void cache_enter(char *host, char **grl, int grc, int belong);
75
76
77void
78netgroup_init()
79{
80	(void) rwlock_init(&cache_lock, USYNC_THREAD, NULL);
81}
82
83/*
84 * Check whether any of the hostnames in clnames are
85 * members (or non-members) of the netgroups in glist.
86 * Since the innetgr lookup is rather expensive, the
87 * result is cached. The cached entry is valid only
88 * for VALID_TIME seconds.  This works well because
89 * typically these lookups occur in clusters when
90 * a client is mounting.
91 *
92 * Note that this routine establishes a host membership
93 * in a list of netgroups - we've no idea just which
94 * netgroup in the list it is a member of.
95 *
96 * glist is a character array containing grc strings
97 * representing netgroup names (optionally prefixed
98 * with '-'). Each string is ended with '\0'  and
99 * followed immediately by the next string.
100 */
101int
102netgroup_check(struct nd_hostservlist *clnames, char  *glist, int grc)
103{
104	char **grl;
105	char *gr;
106	int nhosts = clnames->h_cnt;
107	char *host0, *host;
108	int i, j, n;
109	int response;
110	int belong = 0;
111	static char *domain;
112
113	if (domain == NULL) {
114		int	ssize;
115
116		domain = exmalloc(SYS_NMLN);
117		ssize = sysinfo(SI_SRPC_DOMAIN, domain, SYS_NMLN);
118		if (ssize > SYS_NMLN) {
119			free(domain);
120			domain = exmalloc(ssize);
121			ssize = sysinfo(SI_SRPC_DOMAIN, domain, ssize);
122		}
123		/* Check for error in syscall or NULL domain name */
124		if (ssize <= 1) {
125			syslog(LOG_ERR, "No default domain set");
126			return (0);
127		}
128	}
129
130	grl = calloc(grc, sizeof (char *));
131	if (grl == NULL)
132		return (0);
133
134	for (i = 0, gr = glist; i < grc && !belong; ) {
135		/*
136		 * If the netgroup name has a '-' prepended
137		 * then a match of this name implies a failure
138		 * instead of success.
139		 */
140		response = (*gr != '-') ? 1 : 0;
141
142		/*
143		 * Subsequent names with or without a '-' (but no mix)
144		 * can be grouped together for a single check.
145		 */
146		for (n = 0; i < grc; i++, n++, gr += strlen(gr) + 1) {
147			if ((response && *gr == '-') ||
148			    (!response && *gr != '-'))
149				break;
150
151			grl[n] = response ? gr : gr + 1;
152		}
153
154		host0 = clnames->h_hostservs[0].h_host;
155
156		/*
157		 * If not in cache check the netgroup for each
158		 * of the hosts names (usually just one).
159		 * Enter the result into the cache.
160		 */
161		if (!cache_check(host0, grl, n, &belong)) {
162			for (j = 0; j < nhosts && !belong; j++) {
163				host = clnames->h_hostservs[j].h_host;
164
165				if (__multi_innetgr(n, grl,
166				    1, &host,
167				    0, NULL,
168				    1, &domain))
169					belong = 1;
170			}
171
172			cache_enter(host0, grl, n, belong);
173		}
174	}
175
176	free(grl);
177	return (belong ? response : 0);
178}
179
180/*
181 * Free a cache entry and all entries
182 * further down the chain since they
183 * will also be expired.
184 */
185static void
186cache_free(struct cache_entry *entry)
187{
188	struct cache_entry *ce, *next;
189	int i;
190
191	for (ce = entry; ce; ce = next) {
192		if (ce->cache_host)
193			free(ce->cache_host);
194		for (i = 0; i < ce->cache_grc; i++)
195			if (ce->cache_grl[i])
196				free(ce->cache_grl[i]);
197		if (ce->cache_grl)
198			free(ce->cache_grl);
199		next = ce->cache_next;
200		free(ce);
201	}
202}
203
204/*
205 * Search the entries in the cache chain looking
206 * for an entry with a matching hostname and group
207 * list.  If a match is found then return the "belong"
208 * value which may be 1 or 0 depending on whether the
209 * client is a member of the list or not.  This is
210 * both a positive and negative cache.
211 *
212 * Cache entries have a validity of VALID_TIME seconds.
213 * If we find an expired entry then blow away the entry
214 * and the rest of the chain since entries further down
215 * the chain will be expired too because we always add
216 * new entries to the head of the chain.
217 */
218static int
219cache_check(char *host, char **grl, int grc, int *belong)
220{
221	struct cache_entry *ce, *prev;
222	time_t timenow = time(NULL);
223	int i;
224
225	(void) rw_rdlock(&cache_lock);
226
227	for (ce = cache_head; ce; ce = ce->cache_next) {
228
229		/*
230		 * If we find a stale entry, there can't
231		 * be any valid entries from here on.
232		 * Acquire a write lock, search the chain again
233		 * and delete the stale entry and all following
234		 * entries.
235		 */
236		if (timenow > ce->cache_time) {
237			(void) rw_unlock(&cache_lock);
238			(void) rw_wrlock(&cache_lock);
239
240			for (prev = NULL, ce = cache_head; ce;
241			    prev = ce, ce = ce->cache_next)
242				if (timenow > ce->cache_time)
243					break;
244
245			if (ce != NULL) {
246				if (prev)
247					prev->cache_next = NULL;
248				else
249					cache_head = NULL;
250
251				cache_free(ce);
252			}
253			(void) rw_unlock(&cache_lock);
254
255			return (0);
256		}
257		if (ce->cache_grc != grc)
258			continue;	/* no match */
259
260		if (strcasecmp(host, ce->cache_host) != 0)
261			continue;	/* no match */
262
263		for (i = 0; i < grc; i++)
264			if (strcasecmp(ce->cache_grl[i], grl[i]) != 0)
265				break;	/* no match */
266		if (i < grc)
267			continue;
268
269		*belong = ce->cache_belong;
270		(void) rw_unlock(&cache_lock);
271
272		return (1);
273	}
274
275	(void) rw_unlock(&cache_lock);
276
277	return (0);
278}
279
280/*
281 * Put a new entry in the cache chain by
282 * prepending it to the front.
283 * If there isn't enough memory then just give up.
284 */
285static void
286cache_enter(char *host, char **grl, int grc, int belong)
287{
288	struct cache_entry *entry;
289	int i;
290
291	entry = malloc(sizeof (*entry));
292	if (entry == NULL)
293		return;
294
295	(void) memset((caddr_t)entry, 0, sizeof (*entry));
296	entry->cache_host = strdup(host);
297	if (entry->cache_host == NULL) {
298		cache_free(entry);
299		return;
300	}
301
302	entry->cache_time = time(NULL) + VALID_TIME;
303	entry->cache_belong = belong;
304	entry->cache_grl = malloc(grc * sizeof (char *));
305	if (entry->cache_grl == NULL) {
306		cache_free(entry);
307		return;
308	}
309
310	for (i = 0; i < grc; i++) {
311		entry->cache_grl[i] = strdup(grl[i]);
312		if (entry->cache_grl[i] == NULL) {
313			entry->cache_grc = i;
314			cache_free(entry);
315			return;
316		}
317	}
318
319	entry->cache_grc = grc;
320
321	(void) rw_wrlock(&cache_lock);
322	entry->cache_next = cache_head;
323	cache_head = entry;
324	(void) rw_unlock(&cache_lock);
325}
326
327/*
328 * Full cache flush
329 */
330void
331netgrp_cache_flush(void)
332{
333	(void) rw_wrlock(&cache_lock);
334	cache_free(cache_head);
335	cache_head = NULL;
336	(void) rw_unlock(&cache_lock);
337}
338