release.sh: restore -jJAILDIR option
[minix.git] / lib / libminlib / servxcheck.c
blob330dbc1cadae6684138c161a24f473bc0d2fb586
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 <syslog.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <time.h>
18 #include <sys/ioctl.h>
19 #include <net/hton.h>
20 #include <net/gen/in.h>
21 #include <net/gen/tcp.h>
22 #include <net/gen/tcp_io.h>
23 #include <net/gen/inet.h>
24 #include <net/gen/socket.h>
25 #include <net/gen/netdb.h>
27 /* Default service access file. */
28 static const char *path_servacces = _PATH_SERVACCES;
30 #define WLEN 256
32 static int getword(FILE *fp, char *word)
33 /* Read a word from the file open by 'fp', skip whitespace and comments.
34 * Colon and semicolon are returned as a one character "word". Returns
35 * word[0] or EOF.
38 int c;
39 char *pw;
40 int wc;
42 wc= 0;
43 for (;;) {
44 if ((c= getc(fp)) == EOF) return EOF;
45 if (c == '#') { wc= 1; continue; }
46 if (c == '\n') { wc= 0; continue; }
47 if (wc) continue;
48 if (c <= ' ') continue;
49 break;
52 pw= word;
53 if (c == ':' || c == ';') {
54 *pw++ = c;
55 } else {
56 do {
57 if (pw < word + WLEN-1) *pw++ = c;
58 c= getc(fp);
59 } while (c != EOF && c > ' ' && c != ':' && c != ';');
60 if (c != EOF) ungetc(c, fp);
62 *pw= 0;
63 return word[0];
66 static int netspec(char *word, ipaddr_t *addr, ipaddr_t *mask)
67 /* Try to interpret 'word' as an network spec, e.g. 172.16.102.64/27. */
69 char *slash;
70 int r;
71 static char S32[]= "/32";
73 if (*word == 0) return 0;
75 if ((slash= strchr(word, '/')) == NULL) slash= S32;
77 *slash= 0;
78 r= inet_aton(word, addr);
79 *slash++= '/';
80 if (!r) return 0;
82 r= 0;
83 while ((*slash - '0') < 10u) {
84 r= 10*r + (*slash++ - '0');
85 if (r > 32) return 0;
87 if (*slash != 0 || slash[-1] == '/') return 0;
88 *mask= htonl(r == 0 ? 0L : (0xFFFFFFFFUL >> (32 - r)) << (32 - r));
89 return 1;
92 static int match(const char *word, const char *pattern)
93 /* Match word onto a pattern. Pattern may contain the * wildcard. */
95 unsigned cw, cp;
96 #define lc(c, d) ((((c)= (d)) - 'A') <= ('Z' - 'A') ? (c)+= ('a' - 'A') : 0)
98 for (;;) {
99 (void) lc(cw, *word);
100 (void) lc(cp, *pattern);
102 if (cp == '*') {
103 do pattern++; while (*pattern == '*');
104 (void) lc(cp, *pattern);
105 if (cp == 0) return 1;
107 while (cw != 0) {
108 if (cw == cp && match(word+1, pattern+1)) return 1;
109 word++;
110 (void) lc(cw, *word);
112 return 0;
113 } else
114 if (cw == 0 || cp == 0) {
115 return cw == cp;
116 } else
117 if (cw == cp) {
118 word++;
119 pattern++;
120 } else {
121 return 0;
124 #undef lc
127 static int get_name(ipaddr_t addr, char *name)
128 /* Do a reverse lookup on the remote IP address followed by a forward lookup
129 * to check if the host has that address. Return true if this is so, return
130 * either the true name or the ascii IP address in name[].
133 struct hostent *he;
134 int i;
136 he= gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
137 if (he != NULL) {
138 strcpy(name, he->h_name);
139 he= gethostbyname(name);
141 if (he != NULL && he->h_addrtype == AF_INET) {
142 for (i= 0; he->h_addr_list[i] != NULL; i++) {
143 if (memcmp(he->h_addr_list[i], &addr, sizeof(addr)) == 0) {
144 strcpy(name, he->h_name);
145 return 1;
150 strcpy(name, inet_ntoa(addr));
151 return 0;
154 /* "state" and "log" flags, made to be bitwise comparable. */
155 #define DEFFAIL 0x01
156 #define FAIL (0x02 | DEFFAIL)
157 #define PASS 0x04
159 int servxcheck(unsigned long peer, const char *service,
160 void (*logf)(int pass, const char *name))
162 FILE *fp;
163 char word[WLEN];
164 char name[WLEN];
165 int c;
166 int got_name, slist, seen, explicit, state, log;
167 ipaddr_t addr, mask;
169 /* Localhost? */
170 if ((peer & htonl(0xFF000000)) == htonl(0x7F000000)) return 1;
172 if ((fp= fopen(path_servacces, "r")) == nil) {
173 /* Succeed on error, fail if simply nonexistent. */
174 return (errno != ENOENT);
177 slist= 1; /* Services list (before the colon.) */
178 seen= 0; /* Given service not yet seen. */
179 explicit= 0; /* Service mentioned explicitly. */
180 got_name= -1; /* No reverse lookup done yet. */
181 log= FAIL; /* By default log failures only. */
182 state= DEFFAIL; /* Access denied until we know better. */
184 while ((c= getword(fp, word)) != EOF) {
185 if (c == ':') {
186 slist= 0; /* Switch to access list. */
187 } else
188 if (c == ';') {
189 slist= 1; /* Back to list of services. */
190 seen= 0;
191 } else
192 if (slist) {
193 /* Traverse services list. */
195 if (match(service, word)) {
196 /* Service has been spotted! */
197 if (match(word, service)) {
198 /* Service mentioned without wildcards. */
199 seen= explicit= 1;
200 } else {
201 /* Matched by a wildcard. */
202 if (!explicit) seen= 1;
205 } else {
206 /* Traverse access list. */
208 if (c == 'l' && strcmp(word, "log") == 0) {
209 if (seen) {
210 /* Log failures and successes. */
211 log= FAIL|PASS;
213 continue;
216 if (c != '-' && c != '+') {
217 if (logf == nil) {
218 syslog(LOG_ERR, "%s: strange check word '%s'\n",
219 path_servacces, word);
221 continue;
224 if (seen) {
225 if (state == DEFFAIL) {
226 /* First check determines the default. */
227 state= c == '+' ? FAIL : PASS;
230 if ((state == PASS) == (c == '+')) {
231 /* This check won't change state. */
232 } else
233 if (word[1] == 0) {
234 /* Lone + or - allows all or none. */
235 state= c == '-' ? FAIL : PASS;
236 } else
237 if (netspec(word+1, &addr, &mask)) {
238 /* Remote host is on the specified network? */
239 if (((peer ^ addr) & mask) == 0) {
240 state= c == '-' ? FAIL : PASS;
242 } else {
243 /* Name check. */
244 if (got_name == -1) {
245 got_name= get_name(peer, name);
248 /* Remote host name matches the word? */
249 if (!got_name) {
250 state= FAIL;
251 } else
252 if (match(name, word+1)) {
253 state= c == '-' ? FAIL : PASS;
259 fclose(fp);
261 if ((log & state) != 0) {
262 /* Log the result of the check. */
263 if (got_name == -1) (void) get_name(peer, name);
265 if (logf != nil) {
266 (*logf)(state == PASS, name);
267 } else {
268 syslog(LOG_NOTICE, "service '%s' %s to %s\n",
269 service, state == PASS ? "granted" : "denied", name);
272 return state == PASS;
275 char *servxfile(const char *file)
276 /* Specify a file to use for the access checks other than the default. Return
277 * the old path.
280 const char *oldpath= path_servacces;
281 path_servacces= file;
282 return (char *) oldpath; /* (avoid const poisoning) */