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 57 struct 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 66 static struct cache_entry *cache_head; 67 68 #define VALID_TIME 60 /* seconds */ 69 70 static rwlock_t cache_lock; /* protect the cache chain */ 71 72 static void cache_free(struct cache_entry *entry); 73 static int cache_check(char *host, char **grl, int grc, int *belong); 74 static void cache_enter(char *host, char **grl, int grc, int belong); 75 76 77 void 78 netgroup_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 */ 101 int 102 netgroup_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 */ 185 static void 186 cache_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 */ 218 static int 219 cache_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 */ 285 static void 286 cache_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 */ 330 void 331 netgrp_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