source: trunk/minix/lib/ip/servxcheck.c@ 15

Last change on this file since 15 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 6.9 KB
Line 
1/* servxcheck() - Service access check. Author: Kees J. Bot
2 * 8 Jan 1997
3 */
4#define nil 0
5#define ioctl _ioctl
6#define open _open
7#define write _write
8#define close _close
9#include <sys/types.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <errno.h>
13#include <string.h>
14#include <fcntl.h>
15#include <unistd.h>
16#include <time.h>
17#include <sys/ioctl.h>
18#include <net/hton.h>
19#include <net/gen/in.h>
20#include <net/gen/tcp.h>
21#include <net/gen/tcp_io.h>
22#include <net/gen/inet.h>
23#include <net/gen/socket.h>
24#include <net/gen/netdb.h>
25
26/* Default service access file. */
27static const char *path_servacces = _PATH_SERVACCES;
28
29#define WLEN 256
30
31static int getword(FILE *fp, char *word)
32/* Read a word from the file open by 'fp', skip whitespace and comments.
33 * Colon and semicolon are returned as a one character "word". Returns
34 * word[0] or EOF.
35 */
36{
37 int c;
38 char *pw;
39 int wc;
40
41 wc= 0;
42 for (;;) {
43 if ((c= getc(fp)) == EOF) return EOF;
44 if (c == '#') { wc= 1; continue; }
45 if (c == '\n') { wc= 0; continue; }
46 if (wc) continue;
47 if (c <= ' ') continue;
48 break;
49 }
50
51 pw= word;
52 if (c == ':' || c == ';') {
53 *pw++ = c;
54 } else {
55 do {
56 if (pw < word + WLEN-1) *pw++ = c;
57 c= getc(fp);
58 } while (c != EOF && c > ' ' && c != ':' && c != ';');
59 if (c != EOF) ungetc(c, fp);
60 }
61 *pw= 0;
62 return word[0];
63}
64
65static int netspec(char *word, ipaddr_t *addr, ipaddr_t *mask)
66/* Try to interpret 'word' as an network spec, e.g. 172.16.102.64/27. */
67{
68 char *slash;
69 int r;
70 static char S32[]= "/32";
71
72 if (*word == 0) return 0;
73
74 if ((slash= strchr(word, '/')) == NULL) slash= S32;
75
76 *slash= 0;
77 r= inet_aton(word, addr);
78 *slash++= '/';
79 if (!r) return 0;
80
81 r= 0;
82 while ((*slash - '0') < 10u) {
83 r= 10*r + (*slash++ - '0');
84 if (r > 32) return 0;
85 }
86 if (*slash != 0 || slash[-1] == '/') return 0;
87 *mask= htonl(r == 0 ? 0L : (0xFFFFFFFFUL >> (32 - r)) << (32 - r));
88 return 1;
89}
90
91static int match(const char *word, const char *pattern)
92/* Match word onto a pattern. Pattern may contain the * wildcard. */
93{
94 unsigned cw, cp;
95#define lc(c, d) ((((c)= (d)) - 'A') <= ('Z' - 'A') ? (c)+= ('a' - 'A') : 0)
96
97 for (;;) {
98 lc(cw, *word);
99 lc(cp, *pattern);
100
101 if (cp == '*') {
102 do pattern++; while (*pattern == '*');
103 lc(cp, *pattern);
104 if (cp == 0) return 1;
105
106 while (cw != 0) {
107 if (cw == cp && match(word+1, pattern+1)) return 1;
108 word++;
109 lc(cw, *word);
110 }
111 return 0;
112 } else
113 if (cw == 0 || cp == 0) {
114 return cw == cp;
115 } else
116 if (cw == cp) {
117 word++;
118 pattern++;
119 } else {
120 return 0;
121 }
122 }
123#undef lc
124}
125
126static int get_name(ipaddr_t addr, char *name)
127/* Do a reverse lookup on the remote IP address followed by a forward lookup
128 * to check if the host has that address. Return true if this is so, return
129 * either the true name or the ascii IP address in name[].
130 */
131{
132 struct hostent *he;
133 int i;
134
135 he= gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
136 if (he != NULL) {
137 strcpy(name, he->h_name);
138 he= gethostbyname(name);
139
140 if (he != NULL && he->h_addrtype == AF_INET) {
141 for (i= 0; he->h_addr_list[i] != NULL; i++) {
142 if (memcmp(he->h_addr_list[i], &addr, sizeof(addr)) == 0) {
143 strcpy(name, he->h_name);
144 return 1;
145 }
146 }
147 }
148 }
149 strcpy(name, inet_ntoa(addr));
150 return 0;
151}
152
153/* "state" and "log" flags, made to be bitwise comparable. */
154#define DEFFAIL 0x01
155#define FAIL (0x02 | DEFFAIL)
156#define PASS 0x04
157
158int servxcheck(ipaddr_t peer, const char *service,
159 void (*logf)(int pass, const char *name))
160{
161 FILE *fp;
162 char word[WLEN];
163 char name[WLEN];
164 int c;
165 int got_name, slist, seen, explicit, state, log;
166 ipaddr_t addr, mask;
167
168 /* Localhost? */
169 if ((peer & HTONL(0xFF000000)) == HTONL(0x7F000000)) return 1;
170
171 if ((fp= fopen(path_servacces, "r")) == nil) {
172 /* Succeed on error, fail if simply nonexistent. */
173 return (errno != ENOENT);
174 }
175
176 slist= 1; /* Services list (before the colon.) */
177 seen= 0; /* Given service not yet seen. */
178 explicit= 0; /* Service mentioned explicitly. */
179 got_name= -1; /* No reverse lookup done yet. */
180 log= FAIL; /* By default log failures only. */
181 state= DEFFAIL; /* Access denied until we know better. */
182
183 while ((c= getword(fp, word)) != EOF) {
184 if (c == ':') {
185 slist= 0; /* Switch to access list. */
186 } else
187 if (c == ';') {
188 slist= 1; /* Back to list of services. */
189 seen= 0;
190 } else
191 if (slist) {
192 /* Traverse services list. */
193
194 if (match(service, word)) {
195 /* Service has been spotted! */
196 if (match(word, service)) {
197 /* Service mentioned without wildcards. */
198 seen= explicit= 1;
199 } else {
200 /* Matched by a wildcard. */
201 if (!explicit) seen= 1;
202 }
203 }
204 } else {
205 /* Traverse access list. */
206
207 if (c == 'l' && strcmp(word, "log") == 0) {
208 if (seen) {
209 /* Log failures and successes. */
210 log= FAIL|PASS;
211 }
212 continue;
213 }
214
215 if (c != '-' && c != '+') {
216 if (logf == nil) {
217 fprintf(stderr, "%s: strange check word '%s'\n",
218 path_servacces, word);
219 }
220 continue;
221 }
222
223 if (seen) {
224 if (state == DEFFAIL) {
225 /* First check determines the default. */
226 state= c == '+' ? FAIL : PASS;
227 }
228
229 if ((state == PASS) == (c == '+')) {
230 /* This check won't change state. */
231 } else
232 if (word[1] == 0) {
233 /* Lone + or - allows all or none. */
234 state= c == '-' ? FAIL : PASS;
235 } else
236 if (netspec(word+1, &addr, &mask)) {
237 /* Remote host is on the specified network? */
238 if (((peer ^ addr) & mask) == 0) {
239 state= c == '-' ? FAIL : PASS;
240 }
241 } else {
242 /* Name check. */
243 if (got_name == -1) {
244 got_name= get_name(peer, name);
245 }
246
247 /* Remote host name matches the word? */
248 if (!got_name) {
249 state= FAIL;
250 } else
251 if (match(name, word+1)) {
252 state= c == '-' ? FAIL : PASS;
253 }
254 }
255 }
256 }
257 }
258 fclose(fp);
259
260 if ((log & state) != 0) {
261 /* Log the result of the check. */
262 if (got_name == -1) (void) get_name(peer, name);
263
264 if (logf != nil) {
265 (*logf)(state == PASS, name);
266 } else {
267 int lfd;
268 char line[128+WLEN];
269 time_t t;
270 struct tm *tm;
271 char month[][4]= {
272 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
273 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
274 };
275
276 if ((lfd= open("/usr/adm/log", O_WRONLY|O_APPEND)) != -1) {
277 time(&t);
278 tm= localtime(&t);
279 sprintf(line, "%s %02d %02d:%02d:%02d service '%s' %s to %s\n",
280 month[tm->tm_mon],
281 tm->tm_mday,
282 tm->tm_hour, tm->tm_min, tm->tm_sec,
283 service,
284 state == PASS ? "granted" : "denied",
285 name);
286 (void) write(lfd, line, strlen(line));
287 close(lfd);
288 }
289 }
290 }
291 return state == PASS;
292}
293
294char *servxfile(const char *file)
295/* Specify a file to use for the access checks other than the default. Return
296 * the old path.
297 */
298{
299 const char *oldpath= path_servacces;
300 path_servacces= file;
301 return (char *) oldpath; /* (avoid const poisoning) */
302}
Note: See TracBrowser for help on using the repository browser.