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