1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 /* 12 * Copyright 2012 Nexenta Systems, Inc. All rights reserved. 13 */ 14 #include <stdio.h> 15 #include <sys/types.h> 16 #include <sys/socket.h> 17 #include <netinet/in.h> 18 #include <arpa/inet.h> 19 #include <netdb.h> 20 #include <sys/types.h> 21 #include <sys/stat.h> 22 #include <unistd.h> 23 #include <stdlib.h> 24 #include <strings.h> 25 #include <ctype.h> 26 #include <thread.h> 27 #include <synch.h> 28 #include <time.h> 29 #include <revnetgroup.h> 30 31 #include "files_common.h" 32 33 #define PATH_NETGROUP "/etc/netgroup" 34 35 static uint_t 36 hash_netgr_host(nss_XbyY_args_t *argp, int keyhash, const char *line, 37 int linelen) 38 { 39 const char *end; 40 uint_t hash = 0; 41 int power = 0; 42 43 if (keyhash) { 44 struct nss_innetgr_args *ia; 45 46 ia = (struct nss_innetgr_args *)argp->key.name; 47 48 line = ia->arg[NSS_NETGR_MACHINE].argv[0]; 49 end = line + strlen(line); 50 } else { 51 const char *s; 52 53 end = strchr(line, '\t'); 54 if (end == NULL) 55 end = line; 56 57 s = strstr(line, ".*"); 58 if (s != NULL && s < end) { 59 end = s; 60 } 61 62 } 63 64 while (line < end) { 65 uint_t c = *line++; 66 67 c <<= power++; 68 hash += c; 69 } 70 71 return (hash); 72 } 73 74 static files_hash_func hash_netgr[] = { hash_netgr_host }; 75 76 static files_hash_t hashinfo = { 77 DEFAULTMUTEX, 78 0, 79 NSS_BUFLEN_GROUP, 80 1, 81 hash_netgr 82 }; 83 84 /* ARGSUSED */ 85 static int 86 dummy_netgr(const char *in, int inlen, 87 void *ent, char *buf, int buflen) 88 { 89 return (NSS_STR_PARSE_SUCCESS); 90 } 91 92 /* 93 * return parsed or not 94 */ 95 static boolean_t 96 netgr_extract_hostname(char *str, char **hostname, char **next) 97 { 98 char *s; 99 100 s = strchr(str, '\t'); 101 if (s == NULL || s == str) 102 return (B_FALSE); 103 104 *s = '\0'; 105 *hostname = str; 106 *next = s + 1; 107 108 return (B_TRUE); 109 } 110 111 static boolean_t 112 netgr_hosts_match(const char *hostname, const char *host, const char *domain) 113 { 114 char buf[NSS_BUFSIZ]; 115 uint_t n; 116 117 /* 118 * hostname can be "a.b.c.*" 119 */ 120 n = strlen(hostname); 121 if (n > 0 && hostname[n - 1] == '*') 122 domain = "*"; 123 124 snprintf(buf, sizeof (buf), "%s.%s", host, domain); 125 126 if (strcmp(hostname, buf)) 127 return (B_FALSE); 128 else 129 return (B_TRUE); 130 } 131 132 static boolean_t 133 netgr_netgroups_match(const char *netgroups, const char *group) 134 { 135 char *s; 136 uint_t len; 137 138 /* walk netgroups */ 139 140 s = strstr(netgroups, group); 141 if (s == NULL) 142 return (B_FALSE); 143 144 /* check beginning */ 145 if (s != netgroups && s[-1] != ',') 146 return (B_FALSE); 147 148 /* check end */ 149 len = strlen(group); 150 if (s[len] != ',' && s[len] != '\0') 151 return (B_FALSE); 152 153 return (B_TRUE); 154 } 155 156 /* 157 * Parse string: "{hostname} {netgroup1} ... {netgroup2}" 158 */ 159 static int 160 check_netgr(nss_XbyY_args_t *argp, const char *line, int linelen) 161 { 162 struct nss_innetgr_args *ia; 163 char buf[NSS_BUFSIZ]; 164 const char *host; 165 const char *domain; 166 const char *group; 167 const char *netgroup; 168 char *hostname, *next; 169 170 if (linelen > NSS_BUFSIZ || linelen < 0) 171 return (0); 172 173 /* use only one passed argument */ 174 ia = (struct nss_innetgr_args *)argp->key.name; 175 host = ia->arg[NSS_NETGR_MACHINE].argv[0]; 176 domain = ia->arg[NSS_NETGR_DOMAIN].argv[0]; 177 netgroup = ia->groups.argv[0]; 178 179 strlcpy(buf, line, NSS_BUFSIZ); 180 181 if (!netgr_extract_hostname(buf, &hostname, &next)) 182 return (0); 183 184 if (!netgr_hosts_match(hostname, host, domain)) 185 return (0); 186 187 if (!netgr_netgroups_match(next, netgroup)) 188 return (0); 189 190 return (1); 191 } 192 193 static nss_status_t 194 netgr_in(files_backend_ptr_t be, void *a) 195 { 196 struct nss_innetgr_args *ia = (struct nss_innetgr_args *)a; 197 nss_status_t rc = (nss_status_t)NSS_NOTFOUND; 198 nss_XbyY_args_t xa = {0}; 199 struct stat64 st; 200 int retries; 201 202 retries = 5; 203 while (stat64(PATH_NETGROUP, &st) < 0) { 204 struct timespec delay; 205 206 /* 50 ms */ 207 delay.tv_sec = 0; 208 delay.tv_nsec = 50000000; 209 210 nanosleep(&delay, NULL); 211 if (--retries == 0) 212 goto out; 213 } 214 215 mutex_lock(&hashinfo.fh_lock); 216 217 if ((uint_t)st.st_mtim.tv_sec > (uint_t)hashinfo.fh_mtime.tv_sec) { 218 FILE *fin, *fout; 219 220 /* 221 * netgroup file is more recent than parsed copy 222 */ 223 224 fin = fopen(PATH_NETGROUP, "r"); 225 if (fin == NULL) 226 goto out_unlock; 227 228 fout = fopen(be->filename, "w"); 229 if (fout == NULL) { 230 fclose(fin); 231 goto out_unlock; 232 } 233 234 revnetgroup_handle(fin, fout, B_FALSE); 235 236 fclose(fout); 237 fclose(fin); 238 } 239 240 out_unlock: 241 mutex_unlock(&hashinfo.fh_lock); 242 243 /* some hack to pass arguments to check_netgr */ 244 xa.key.name = (char *)ia; 245 xa.str2ent = dummy_netgr; 246 rc = _nss_files_XY_hash(be, &xa, 1, &hashinfo, 0, check_netgr); 247 248 out: 249 ia->status = (rc == NSS_SUCCESS) ? NSS_NETGR_FOUND : NSS_NETGR_NO; 250 return (rc); 251 } 252 253 /*ARGSUSED*/ 254 nss_status_t 255 netgr_files_destr(files_backend_ptr_t be, void *dummy) 256 { 257 nss_status_t status; 258 259 unlink(be->filename); 260 free((void *)be->filename); 261 status = _nss_files_destr(be, dummy); 262 return (status); 263 } 264 265 static char * 266 create_uniq_filename(void) 267 { 268 char *filename; 269 int dummy_fd; 270 271 filename = malloc(PATH_MAX); 272 if (filename == NULL) 273 return (NULL); 274 275 strlcpy(filename, "/tmp/revnetgroup-XXXXXX", PATH_MAX); 276 277 dummy_fd = mkstemp(filename); 278 close(dummy_fd); 279 if (dummy_fd < 0) { 280 free(filename); 281 return (NULL); 282 } 283 return (filename); 284 } 285 286 static files_backend_op_t netgr_ops[] = { 287 netgr_files_destr, 288 NULL, /* No endent, because no setent */ 289 NULL, /* No setent */ 290 NULL, /* No getent_netdb */ 291 netgr_in, /* innetgr */ 292 NULL, /* No setnetgrent */ 293 }; 294 295 /*ARGSUSED*/ 296 nss_backend_t * 297 _nss_files_netgroup_constr(const char *dummy1, 298 const char *dummy2, const char *dummy3) 299 { 300 char *filename; 301 302 filename = create_uniq_filename(); 303 if (filename == NULL) 304 return (NULL); 305 306 return (_nss_files_constr(netgr_ops, 307 sizeof (netgr_ops) / sizeof (netgr_ops[0]), 308 filename, 309 NSS_LINELEN_NETWORKS, 310 NULL)); 311 }