1 /* servxcheck() - Service access check. Author: Kees J. Bot
18 #include <sys/ioctl.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
;
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
44 if ((c
= getc(fp
)) == EOF
) return EOF
;
45 if (c
== '#') { wc
= 1; continue; }
46 if (c
== '\n') { wc
= 0; continue; }
48 if (c
<= ' ') continue;
53 if (c
== ':' || c
== ';') {
57 if (pw
< word
+ WLEN
-1) *pw
++ = c
;
59 } while (c
!= EOF
&& c
> ' ' && c
!= ':' && c
!= ';');
60 if (c
!= EOF
) ungetc(c
, fp
);
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. */
71 static char S32
[]= "/32";
73 if (*word
== 0) return 0;
75 if ((slash
= strchr(word
, '/')) == NULL
) slash
= S32
;
78 r
= inet_aton(word
, addr
);
83 while ((*slash
- '0') < 10u) {
84 r
= 10*r
+ (*slash
++ - '0');
87 if (*slash
!= 0 || slash
[-1] == '/') return 0;
88 *mask
= htonl(r
== 0 ? 0L : (0xFFFFFFFFUL
>> (32 - r
)) << (32 - r
));
92 static int match(const char *word
, const char *pattern
)
93 /* Match word onto a pattern. Pattern may contain the * wildcard. */
96 #define lc(c, d) ((((c)= (d)) - 'A') <= ('Z' - 'A') ? (c)+= ('a' - 'A') : 0)
100 (void) lc(cp
, *pattern
);
103 do pattern
++; while (*pattern
== '*');
104 (void) lc(cp
, *pattern
);
105 if (cp
== 0) return 1;
108 if (cw
== cp
&& match(word
+1, pattern
+1)) return 1;
110 (void) lc(cw
, *word
);
114 if (cw
== 0 || cp
== 0) {
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[].
136 he
= gethostbyaddr((char *) &addr
, sizeof(addr
), AF_INET
);
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
);
150 strcpy(name
, inet_ntoa(addr
));
154 /* "state" and "log" flags, made to be bitwise comparable. */
156 #define FAIL (0x02 | DEFFAIL)
159 int servxcheck(unsigned long peer
, const char *service
,
160 void (*logf
)(int pass
, const char *name
))
166 int got_name
, slist
, seen
, explicit, state
, log
;
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
) {
186 slist
= 0; /* Switch to access list. */
189 slist
= 1; /* Back to list of services. */
193 /* Traverse services list. */
195 if (match(service
, word
)) {
196 /* Service has been spotted! */
197 if (match(word
, service
)) {
198 /* Service mentioned without wildcards. */
201 /* Matched by a wildcard. */
202 if (!explicit) seen
= 1;
206 /* Traverse access list. */
208 if (c
== 'l' && strcmp(word
, "log") == 0) {
210 /* Log failures and successes. */
216 if (c
!= '-' && c
!= '+') {
218 syslog(LOG_ERR
, "%s: strange check word '%s'\n",
219 path_servacces
, word
);
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. */
234 /* Lone + or - allows all or none. */
235 state
= c
== '-' ? FAIL
: PASS
;
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
;
244 if (got_name
== -1) {
245 got_name
= get_name(peer
, name
);
248 /* Remote host name matches the word? */
252 if (match(name
, word
+1)) {
253 state
= c
== '-' ? FAIL
: PASS
;
261 if ((log
& state
) != 0) {
262 /* Log the result of the check. */
263 if (got_name
== -1) (void) get_name(peer
, name
);
266 (*logf
)(state
== PASS
, name
);
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
280 const char *oldpath
= path_servacces
;
281 path_servacces
= file
;
282 return (char *) oldpath
; /* (avoid const poisoning) */