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