1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <unistd.h>
4 #include <time.h>
5 #include "ipsvd_check.h"
6 #include "ipsvd_log.h"
7 #include "ipsvd_fmt.h"
8 #include "error.h"
9 #include "stralloc.h"
10 #include "strerr.h"
11 #include "byte.h"
12 #include "scan.h"
13 #include "str.h"
14 #include "openreadclose.h"
15 #include "open.h"
16 #include "cdb.h"
17 #include "pathexec.h"
18 #include "dns.h"
19 #include "ip4.h"
20 
21 extern const char *progname;
22 static stralloc sa ={0};
23 static stralloc ips ={0};
24 static stralloc fqdn ={0};
25 static stralloc msg ={0};
26 static stralloc forwardfn ={0};
27 static stralloc moredata ={0};
28 static char *forward =0;
29 unsigned long phccmax;
30 char *phccmsg;
31 
32 static struct cdb c;
33 static int fd;
34 static uint32 dlen;
35 
ipsvd_instruct(stralloc * inst,stralloc * match,char * ip)36 int ipsvd_instruct(stralloc *inst, stralloc *match, char *ip) {
37   char *insts;
38   unsigned int instslen;
39   int delim;
40   int i, j;
41   int rc =IPSVD_DEFAULT;
42 
43   if (inst->s && inst->len) {
44     insts =inst->s; instslen =inst->len;
45     while ((i =byte_chr(insts, instslen, 0)) < instslen) {
46       switch(*insts) {
47       case '+':
48         if ((delim =str_chr(insts, '=')) <= 1) break; /* empty inst */
49         if (insts[delim] == '=') {
50           insts[delim] =0;
51           if (! pathexec_env(insts +1, insts +delim +1)) return(-1);
52           insts[delim] ='=';
53         }
54         else if (! pathexec_env(insts +1, 0)) return(-1);
55         break;
56       case 'C':
57         if (! phccmax) break;
58         delim =scan_ulong(insts +1, &phccmax);
59         if (insts[delim +1] == ':') {
60           if (ipsvd_fmt_msg(&msg, insts +delim +2) == -1) return(-1);
61           if (! stralloc_0(&msg)) return(-1);
62           phccmsg =msg.s;
63         }
64         break;
65       case '=':
66         if (ip && (rc != IPSVD_INSTRUCT)) {
67           unsigned int next;
68 
69           rc =IPSVD_DENY;
70           next =str_chr(insts +1, ':'); ++next;
71           if ((next == 2) && (insts[1] == '0')) {
72             if (! stralloc_copys(&sa, ip)) return(-1);
73           }
74           else
75             if (! stralloc_copyb(&sa, insts +1, next -1)) return(-1);
76           if (insts[next] != 0) ++next;
77 
78           if ((dns_ip4(&ips, &sa) == -1) || (ips.len < 4))
79             if (dns_ip4_qualify(&ips, &fqdn, &sa) == -1) {
80               if (! stralloc_0(&sa)) return(-1);
81               strerr_warn5(progname, ": warning: ",
82                            "unable to look up ip address: ", sa.s,
83                            ": ", &strerr_sys);
84               break;
85             }
86           if (ips.len < 4) {
87             if (! stralloc_0(&sa)) return(-1);
88             strerr_warn4(progname, ": warning: ",
89                          "unable to look up ip address: ", sa.s, 0);
90             break;
91           }
92           for (j =0; j +4 <= ips.len; j +=4) {
93             char tmp[IP4_FMT];
94 
95             tmp[ipsvd_fmt_ip(tmp, ips.s +j)] =0;
96             if (str_equal(tmp, ip)) {
97               inst->len =insts -inst->s +i +1;
98               if (insts[next]) {
99                 forward =insts +next;
100                 return(IPSVD_FORWARD);
101               }
102               return(IPSVD_INSTRUCT);
103             }
104           }
105         }
106         break;
107       case 0: case '#': /* skip empty line and comment */
108         break;
109       default:
110         strerr_warn6(progname, ": warning: ",
111                      "bad instruction: ", match->s, ": ", insts, 0);
112       }
113       insts +=i +1;
114       instslen -=i +1;
115     }
116   }
117   if (rc == IPSVD_DEFAULT) return(IPSVD_INSTRUCT);
118   return(rc);
119 }
120 
ipsvd_check_direntry(stralloc * d,stralloc * m,char * ip,time_t now,unsigned long t,int * rc)121 int ipsvd_check_direntry(stralloc *d, stralloc *m, char *ip,
122                          time_t now, unsigned long t, int *rc) {
123   int i;
124   struct stat s;
125 
126   if (stat(m->s, &s) != -1) {
127     if (t && (s.st_mode & S_IWUSR) && (now >= s.st_atime))
128       if ((now -s.st_atime) >= t) {
129         if (unlink(m->s) == -1)
130           strerr_warn4(progname, ": warning: unable to unlink: ", m->s, ": ",
131                        &strerr_sys);
132         return(0);
133       }
134     if (! (s.st_mode & S_IXUSR) && ! (s.st_mode & S_IRUSR)) {
135       *rc =IPSVD_DENY; return(1);
136     }
137     if (s.st_mode & S_IXUSR) {
138       if (openreadclose(m->s, d, 256) <= 0) return(-1);
139       if (d->len && (d->s[d->len -1] == '\n')) d->len--;
140       if (! stralloc_0(d)) return(-1);
141       *rc =IPSVD_EXEC;
142       return(1);
143     }
144     if (s.st_mode & S_IRUSR) {
145       if (openreadclose(m->s, d, 256) <= 0) return(-1);
146       if (d->len && (d->s[d->len -1] == '\n')) d->len--;
147       for (i =0; i < d->len; i++) if (d->s[i] == '\n') d->s[i] =0;
148       if (! stralloc_0(d)) return(-1);
149       if ((*rc =ipsvd_instruct(d, m, ip)) == -1) return(-1);
150       return(1);
151     }
152     if (! stralloc_copys(m, "")) return(-1);
153     if (! stralloc_0(m)) return(-1);
154     *rc =IPSVD_DEFAULT;
155     return(1);
156   }
157   else if (errno != error_noent) return(-1);
158   return(0);
159 }
160 
ipsvd_check_dir(stralloc * data,stralloc * match,char * dir,char * ip,char * name,unsigned long timeout)161 int ipsvd_check_dir(stralloc *data, stralloc *match, char *dir,
162                     char *ip, char *name, unsigned long timeout) {
163   struct stat s;
164   int i;
165   int rc;
166   int ok;
167   int base;
168   time_t now =0;
169 
170   if (stat(dir, &s) == -1) return(IPSVD_ERR);
171   if (timeout) now =time((time_t*)0);
172   if (! stralloc_copys(match, dir)) return(-1);
173   if (! stralloc_cats(match, "/")) return(-1);
174   base =match->len;
175   /* ip */
176   if (ip) {
177     if (! stralloc_cats(match, ip)) return(-1);
178     if (! stralloc_0(match)) return(-1);
179     data->len =0;
180     for (;;) {
181       ok =ipsvd_check_direntry(data, match, ip, now, timeout, &rc);
182       if (ok == -1) return(-1);
183       if (ok) {
184         if (rc == IPSVD_FORWARD) goto forwarded;
185         return(rc);
186       }
187       if ((i =byte_rchr(match->s, match->len, '.')) == match->len) break;
188       if (i <= base) break;
189       match->s[i] =0; match->len =i +1;
190     }
191   }
192   /* host */
193   if (name) {
194     for (;;) {
195       if (! *name || (*name == '.')) break;
196       match->len =base;
197       if (! stralloc_cats(match, name)) return(-1);
198       if (! stralloc_0(match)) return(-1);
199 
200       ok =ipsvd_check_direntry(data, match, ip, now, timeout, &rc);
201       if (ok == -1) return(-1);
202       if (ok) {
203         if (rc == IPSVD_FORWARD) goto forwarded;
204         return(rc);
205       }
206       if ((i =byte_chr(name, str_len(name), '.')) == str_len(name)) break;
207       name +=i +1;
208     }
209   }
210   /* default */
211   match->len =base;
212   if (! stralloc_cats(match, "0")) return(-1);
213   if (! stralloc_0(match)) return(-1);
214 
215   ok =ipsvd_check_direntry(data, match, ip, now, timeout, &rc);
216   if (ok == -1) return(-1);
217   if (ok) {
218     if (rc == IPSVD_FORWARD) goto forwarded;
219     return(rc);
220   }
221   if (! stralloc_copys(match, "")) return(-1);
222   if (! stralloc_0(match)) return(-1);
223   return(IPSVD_DEFAULT);
224 
225  forwarded:
226   if (! stralloc_copyb(&forwardfn, match->s, base)) return(-1);
227   if (! stralloc_cats(&forwardfn, forward)) return(-1);
228   if (! stralloc_0(&forwardfn)) return(-1);
229   ok =ipsvd_check_direntry(&moredata, &forwardfn, 0, now, timeout, &rc);
230   if (ok == -1) return(-1);
231   --match->len;
232   if (! stralloc_cats(match, ",")) return(-1);
233   if (! stralloc_cats(match, forwardfn.s +base)) return(-1);
234   if (! stralloc_0(match)) return(-1);
235   if (ok) {
236     if (rc == IPSVD_EXEC)
237       data->len =0;
238     else
239       data->s[data->len -1] =',';
240     if (! stralloc_cat(data, &moredata)) return(-1);
241     return(rc);
242   }
243   strerr_warn4(progname, ": warning: ", match->s, ": not found", 0);
244   return(IPSVD_DENY);
245 }
246 
ipsvd_check_cdbentry(stralloc * d,stralloc * m,char * ip,int * rc)247 int ipsvd_check_cdbentry(stralloc *d, stralloc *m, char *ip, int *rc) {
248   switch(cdb_find(&c, m->s, m->len -1)) {
249     case -1: return(-1);
250     case 1:
251       dlen =cdb_datalen(&c);
252       if (! stralloc_ready(d, dlen)) return(-1);
253       if (cdb_read(&c, d->s, dlen, cdb_datapos(&c)) == -1) return(-1);
254       if (! dlen) return(-1);
255       switch(d->s[dlen -1]) {
256       case 'D':
257         *rc =IPSVD_DENY;
258         return(1);
259       case 'X':
260         d->s[dlen -1] =0; d->len =dlen;
261         *rc =IPSVD_EXEC;
262         return(1);
263       case 'I':
264         d->s[dlen -1] =0; d->len =dlen;
265         if ((*rc =ipsvd_instruct(d, m, ip)) == -1) return(-1);
266         return(1);
267       }
268     }
269   return(0);
270 }
271 
ipsvd_check_cdb(stralloc * data,stralloc * match,char * cdb,char * ip,char * name,unsigned long unused)272 int ipsvd_check_cdb(stralloc *data, stralloc *match, char *cdb,
273                     char *ip, char *name, unsigned long unused) {
274   int ok;
275   int i;
276   int rc;
277 
278   if ((fd =open_read(cdb)) == -1) return(IPSVD_ERR);
279   cdb_init(&c, fd);
280   if (! stralloc_copys(match, ip)) return(-1);
281   if (! stralloc_0(match)) return(-1);
282   data->len =0;
283   /* ip */
284   for (;;) {
285     ok =ipsvd_check_cdbentry(data, match, ip, &rc);
286     if (ok == -1) return(-1);
287     if (ok) {
288       if (rc == IPSVD_FORWARD) goto forwarded;
289       close(fd);
290       return(rc);
291     }
292     if ((i =byte_rchr(match->s, match->len, '.')) == match->len) break;
293     match->s[i] =0; match->len =i +1;
294   }
295   /* host */
296   if (name) {
297     for (;;) {
298       if (! *name || (*name == '.')) break;
299       if (! stralloc_copys(match, name)) return(-1);
300       if (! stralloc_0(match)) return(-1);
301       ok =ipsvd_check_cdbentry(data, match, ip, &rc);
302       if (ok == -1) return(-1);
303       if (ok) {
304         if (rc == IPSVD_FORWARD) goto forwarded;
305         close(fd);
306         return(rc);
307       }
308       if ((i =byte_chr(name, str_len(name), '.')) == str_len(name)) break;
309       name +=i +1;
310     }
311   }
312   /* default */
313   if (! stralloc_copys(match, "0")) return(-1);
314   if (! stralloc_0(match)) return(-1);
315   ok =ipsvd_check_cdbentry(data, match, ip, &rc);
316   if (ok == -1) return(-1);
317   if (ok) {
318     if (rc == IPSVD_FORWARD) goto forwarded;
319     close(fd);
320     return(rc);
321   }
322   if (! stralloc_copys(match, "")) return(-1);
323   if (! stralloc_0(match)) return(-1);
324   close(fd);
325   return(IPSVD_DEFAULT);
326 
327  forwarded:
328   if (! stralloc_copys(&forwardfn, forward)) return(-1);
329   if (! stralloc_0(&forwardfn)) return(-1);
330   ok =ipsvd_check_cdbentry(&moredata, &forwardfn, 0, &rc);
331   close(fd);
332   if (ok == -1) return(-1);
333   --match->len;
334   if (! stralloc_cats(match, ",")) return(-1);
335   if (! stralloc_cats(match, forwardfn.s)) return(-1);
336   if (! stralloc_0(match)) return(-1);
337   if (ok) {
338     if (rc == IPSVD_EXEC)
339       data->len =0;
340     else
341       data->s[data->len -1] =',';
342     if (! stralloc_cat(data, &moredata)) return(-1);
343     return(rc);
344   }
345   strerr_warn4(progname, ": warning: ", match->s, ": not found", 0);
346   return(IPSVD_DENY);
347 }
348 
ipsvd_check(int c,stralloc * data,stralloc * match,char * db,char * ip,char * name,unsigned long timeout)349 int ipsvd_check(int c, stralloc *data, stralloc *match, char *db,
350                 char *ip, char *name, unsigned long timeout) {
351   if (c)
352     return(ipsvd_check_cdb(data, match, db, ip, name, 0));
353   else
354     return(ipsvd_check_dir(data, match, db, ip, name, timeout));
355 }
356