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 }