9c5675e2d77523f126bbdb717e74e1e838f34acf
[openafs.git] / src / auth / netrestrict.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Network utility functions
12  * Parsing NetRestrict file and filtering IP addresses
13  */
14
15 #include <afsconfig.h>
16 #include <afs/param.h>
17
18 #include <roken.h>
19 #include <ctype.h>
20
21 #include <rx/rx.h>
22 #include <afs/dirpath.h>
23
24 #include "cellconfig.h"
25
26 #define AFS_IPINVALID        0xffffffff /* invalid IP address */
27 #define AFS_IPINVALIDIGNORE  0xfffffffe /* no input given to extractAddr */
28 #define MAX_NETFILE_LINE       2048     /* length of a line in the netrestrict file */
29 #define MAXIPADDRS             1024     /* from afsd.c */
30
31 static int ParseNetInfoFile_int(afs_uint32 *, afs_uint32 *, afs_uint32 *,
32                          int, char reason[], const char *,
33                          int);
34 /*
35  * The line parameter is a pointer to a buffer containing a string of
36  * bytes of the form
37 ** w.x.y.z      # machineName
38  * returns the network interface IP Address in NBO
39  */
40 afs_uint32
41 extract_Addr(char *line, int maxSize)
42 {
43     char bytes[4][32];
44     int i = 0, n = 0;
45     char *endPtr;
46     afs_uint32 val[4];
47     afs_uint32 retval = 0;
48
49     /* skip empty spaces */
50     while (isspace(*line) && maxSize) {
51         line++;
52         maxSize--;
53     }
54     /* skip empty lines */
55     if (!maxSize || !*line)
56         return AFS_IPINVALIDIGNORE;
57
58     for (n = 0; n < 4; n++) {
59         while ((*line != '.') && !isspace(*line) && maxSize) {  /* extract nth byte */
60             if (!isdigit(*line))
61                 return AFS_IPINVALID;
62             if (i > 31)
63                 return AFS_IPINVALID;   /* no space */
64             bytes[n][i++] = *line++;
65             maxSize--;
66         }                       /* while */
67         if (!maxSize)
68             return AFS_IPINVALID;
69         bytes[n][i] = 0;
70         i = 0, line++;
71         errno = 0;
72         val[n] = strtol(bytes[n], &endPtr, 10);
73         if ((val[n] == 0) && (errno != 0 || bytes[n] == endPtr))        /* no conversion */
74             return AFS_IPINVALID;
75     }                           /* for */
76
77     retval = (val[0] << 24) | (val[1] << 16) | (val[2] << 8) | val[3];
78     return htonl(retval);
79 }
80
81
82
83
84 /* parseNetRestrictFile()
85  * Get a list of IP addresses for this host removing any address found
86  * in the config file (fileName parameter): /usr/vice/etc/NetRestrict
87  * for clients and /usr/afs/local/NetRestrict for servers.
88  *
89  * Returns the number of valid addresses in outAddrs[] and count in
90  * nAddrs.  Returns 0 on success; or 1 if the config file was not
91  * there or empty (we still return the host's IP addresses). Returns
92  * -1 on fatal failure with reason in the reason argument (so the
93  * caller can choose to ignore the entire file but should write
94  * something to a log file).
95  *
96  * All addresses should be in NBO (as returned by rx_getAllAddrMaskMtu() and
97  * parsed by extract_Addr().
98  */
99 /*
100   afs_uint32  outAddrs[];          * output address array *
101   afs_uint32  *mask, *mtu;         * optional mask and mtu *
102   afs_uint32 maxAddrs;             * max number of addresses *
103   afs_uint32 *nAddrs;              * number of Addresses in output array *
104   char       reason[];             * reason for failure *
105   const char *fileName;            * filename to parse *
106 */
107
108 static int
109 parseNetRestrictFile_int(afs_uint32 outAddrs[], afs_uint32 * mask,
110                          afs_uint32 * mtu, afs_uint32 maxAddrs,
111                          afs_uint32 * nAddrs, char reason[],
112                          const char *fileName, const char *fileName_ni)
113 {
114     FILE *fp;
115     char line[MAX_NETFILE_LINE];
116     int lineNo, usedfile = 0;
117     afs_uint32 i, neaddrs, nOutaddrs;
118     afs_uint32 addr, eAddrs[MAXIPADDRS], eMask[MAXIPADDRS], eMtu[MAXIPADDRS];
119
120     osi_Assert(outAddrs);
121     osi_Assert(reason);
122     osi_Assert(fileName);
123     osi_Assert(nAddrs);
124     if (mask)
125         osi_Assert(mtu);
126
127     /* Initialize */
128     *nAddrs = 0;
129     for (i = 0; i < maxAddrs; i++)
130         outAddrs[i] = 0;
131     strcpy(reason, "");
132
133     /* get all network interfaces from the kernel */
134     neaddrs = rx_getAllAddrMaskMtu(eAddrs, eMask, eMtu, MAXIPADDRS);
135     if (neaddrs <= 0) {
136         sprintf(reason, "No existing IP interfaces found");
137         return -1;
138     }
139     i = 0;
140     if ((neaddrs < MAXIPADDRS) && fileName_ni)
141         i = ParseNetInfoFile_int(&(eAddrs[neaddrs]), &(eMask[neaddrs]),
142                                  &(eMtu[neaddrs]), MAXIPADDRS-neaddrs, reason,
143                                  fileName_ni, 1);
144
145     if (i > 0)
146         neaddrs += i;
147
148     if ((fp = fopen(fileName, "r")) == 0) {
149         sprintf(reason, "Could not open file %s for reading:%s", fileName,
150                 strerror(errno));
151         goto done;
152     }
153
154     /* For each line in the NetRestrict file */
155     lineNo = 0;
156     usedfile = 0;
157     while (fgets(line, MAX_NETFILE_LINE, fp) != NULL) {
158         lineNo++;               /* input line number */
159         addr = extract_Addr(line, strlen(line));
160         if (addr == AFS_IPINVALID) {    /* syntactically invalid */
161             fprintf(stderr, "%s : line %d : parse error - invalid IP\n",
162                     fileName, lineNo);
163             continue;
164         }
165         if (addr == AFS_IPINVALIDIGNORE) {      /* ignore error */
166             fprintf(stderr, "%s : line %d : invalid address ... ignoring\n",
167                     fileName, lineNo);
168             continue;
169         }
170         usedfile = 1;
171
172         /* Check if we need to exclude this address */
173         for (i = 0; i < neaddrs; i++) {
174             if (eAddrs[i] && (eAddrs[i] == addr)) {
175                 eAddrs[i] = 0;  /* Yes - exclude it by zeroing it for now */
176             }
177         }
178     }                           /* while */
179
180     fclose(fp);
181
182     if (!usedfile) {
183         sprintf(reason, "No valid IP addresses in %s\n", fileName);
184         goto done;
185     }
186
187   done:
188     /* Collect the addresses we have left to return */
189     nOutaddrs = 0;
190     for (i = 0; i < neaddrs; i++) {
191         if (!eAddrs[i])
192             continue;
193         outAddrs[nOutaddrs] = eAddrs[i];
194         if (mask) {
195             mask[nOutaddrs] = eMask[i];
196             mtu[nOutaddrs] = eMtu[i];
197         }
198         if (++nOutaddrs >= maxAddrs)
199             break;
200     }
201     if (nOutaddrs == 0) {
202         sprintf(reason, "No addresses to use after parsing %s", fileName);
203         return -1;
204     }
205     *nAddrs = nOutaddrs;
206     return (usedfile ? 0 : 1);  /* 0=>used the file.  1=>didn't use file */
207 }
208
209 int
210 afsconf_ParseNetRestrictFile(afs_uint32 outAddrs[], afs_uint32 * mask,
211                              afs_uint32 * mtu, afs_uint32 maxAddrs,
212                              afs_uint32 * nAddrs, char reason[],
213                              const char *fileName)
214 {
215     return parseNetRestrictFile_int(outAddrs, mask, mtu, maxAddrs, nAddrs, reason, fileName, NULL);
216 }
217
218 /*
219  * this function reads in stuff from InterfaceAddr file in
220  * /usr/vice/etc ( if it exists ) and verifies the addresses
221  * specified.
222  * 'final' contains all those addresses that are found to
223  * be valid. This function returns the number of valid
224  * interface addresses. Pulled out from afsd.c
225  */
226 static int
227 ParseNetInfoFile_int(afs_uint32 * final, afs_uint32 * mask, afs_uint32 * mtu,
228                      int max, char reason[], const char *fileName,
229                      int fakeonly)
230 {
231
232     afs_uint32 existingAddr[MAXIPADDRS], existingMask[MAXIPADDRS],
233         existingMtu[MAXIPADDRS];
234     char line[MAX_NETFILE_LINE];
235     FILE *fp;
236     int i, existNu, count = 0;
237     afs_uint32 addr;
238     int lineNo = 0;
239     int l;
240
241     osi_Assert(fileName);
242     osi_Assert(final);
243     osi_Assert(mask);
244     osi_Assert(mtu);
245     osi_Assert(reason);
246
247     /* get all network interfaces from the kernel */
248     existNu =
249         rx_getAllAddrMaskMtu(existingAddr, existingMask, existingMtu,
250                               MAXIPADDRS);
251     if (existNu < 0)
252         return existNu;
253
254     if ((fp = fopen(fileName, "r")) == 0) {
255         /* If file does not exist or is not readable, then
256          * use all interface addresses.
257          */
258         sprintf(reason,
259                 "Failed to open %s(%s)\nUsing all configured addresses\n",
260                 fileName, strerror(errno));
261         for (i = 0; i < existNu; i++) {
262             final[i] = existingAddr[i];
263             mask[i] = existingMask[i];
264             mtu[i] = existingMtu[i];
265         }
266         return existNu;
267     }
268
269     /* For each line in the NetInfo file */
270     while (fgets(line, MAX_NETFILE_LINE, fp) != NULL) {
271         int fake = 0;
272
273         /* See if first char is an 'F' for fake */
274         /* Added to allow the fileserver to advertise fake IPS for use with
275          * the translation tables for NAT-like firewalls - defect 12462 */
276         for (fake = 0; ((fake < strlen(line)) && isspace(line[fake]));
277              fake++);
278         if ((fake < strlen(line))
279             && ((line[fake] == 'f') || (line[fake] == 'F'))) {
280             fake++;
281         } else {
282             fake = 0;
283         }
284
285         lineNo++;               /* input line number */
286         addr = extract_Addr(&line[fake], strlen(&line[fake]));
287
288         if (addr == AFS_IPINVALID) {    /* syntactically invalid */
289             fprintf(stderr, "afs:%s : line %d : parse error\n", fileName,
290                     lineNo);
291             continue;
292         }
293         if (addr == AFS_IPINVALIDIGNORE) {      /* ignore error */
294             continue;
295         }
296
297         /* See if it is an address that really exists */
298         for (i = 0; i < existNu; i++) {
299             if (existingAddr[i] == addr)
300                 break;
301         }
302         if ((i >= existNu) && (!fake))
303             continue;           /* not found/fake - ignore */
304
305         /* Check if it is a duplicate address we alread have */
306         for (l = 0; l < count; l++) {
307             if (final[l] == addr)
308                 break;
309         }
310         if (l < count) {
311             fprintf(stderr, "afs:%x specified twice in NetInfo file\n",
312                     ntohl(addr));
313             continue;           /* duplicate addr - ignore */
314         }
315
316         if (count > max) {      /* no more space */
317             fprintf(stderr,
318                     "afs:Too many interfaces. The current kernel configuration supports a maximum of %d interfaces\n",
319                     max);
320         } else if (fake) {
321             if (!fake)
322                 fprintf(stderr, "Client (2) also has address %s\n", line);
323             final[count] = addr;
324             mask[count] = 0xffffffff;
325             mtu[count] = htonl(1500);
326             count++;
327         } else if (!fakeonly) {
328             final[count] = existingAddr[i];
329             mask[count] = existingMask[i];
330             mtu[count] = existingMtu[i];
331             count++;
332         }
333     }                           /* while */
334
335     /* in case of any error, we use all the interfaces present */
336     if (count <= 0) {
337         sprintf(reason,
338                 "Error in reading/parsing Interface file\nUsing all configured interface addresses \n");
339         for (i = 0; i < existNu; i++) {
340             final[i] = existingAddr[i];
341             mask[i] = existingMask[i];
342             mtu[i] = existingMtu[i];
343         }
344         return existNu;
345     }
346     return count;
347 }
348
349 int
350 afsconf_ParseNetInfoFile(afs_uint32 * final, afs_uint32 * mask, afs_uint32 * mtu,
351                          int max, char reason[], const char *fileName)
352 {
353     return ParseNetInfoFile_int(final, mask, mtu, max, reason, fileName, 0);
354 }
355
356 /*
357  * Given two arrays of addresses, masks and mtus find the common ones
358  * and return them in the first buffer. Return number of common
359  * entries.
360  */
361 static int
362 filterAddrs(afs_uint32 addr1[], afs_uint32 addr2[], afs_uint32 mask1[],
363             afs_uint32 mask2[], afs_uint32 mtu1[], afs_uint32 mtu2[], int n1,
364             int n2)
365 {
366     afs_uint32 taddr[MAXIPADDRS];
367     afs_uint32 tmask[MAXIPADDRS];
368     afs_uint32 tmtu[MAXIPADDRS];
369     int count = 0, i = 0, j = 0, found = 0;
370
371     osi_Assert(addr1);
372     osi_Assert(addr2);
373     osi_Assert(mask1);
374     osi_Assert(mask2);
375     osi_Assert(mtu1);
376     osi_Assert(mtu2);
377
378     for (i = 0; i < n1; i++) {
379         found = 0;
380         for (j = 0; j < n2; j++) {
381             if (addr1[i] == addr2[j]) {
382                 found = 1;
383                 break;
384             }
385         }
386
387         /* Always mask loopback address */
388         if (found && rx_IsLoopbackAddr(addr1[i]))
389             found = 0;
390
391         if (found) {
392             taddr[count] = addr1[i];
393             tmask[count] = mask1[i];
394             tmtu[count] = mtu1[i];
395             count++;
396         }
397     }
398     /* copy everything into addr1, mask1 and mtu1 */
399     for (i = 0; i < count; i++) {
400         addr1[i] = taddr[i];
401         if (mask1) {
402             mask1[i] = tmask[i];
403             mtu1[i] = tmtu[i];
404         }
405     }
406     /* and zero out the rest */
407     for (i = count; i < n1; i++) {
408         addr1[i] = 0;
409         if (mask1) {
410             mask1[i] = 0;
411             mtu1[i] = 0;
412         }
413     }
414     return count;
415 }
416
417 /*
418  * parse both netinfo and netrerstrict files and return the final
419  * set of IP addresses to use
420  */
421 /* max - Entries in addrbuf, maskbuf and mtubuf */
422 int
423 afsconf_ParseNetFiles(afs_uint32 addrbuf[], afs_uint32 maskbuf[],
424                       afs_uint32 mtubuf[], afs_uint32 max, char reason[],
425                       const char *niFileName, const char *nrFileName)
426 {
427     afs_uint32 addrbuf1[MAXIPADDRS], maskbuf1[MAXIPADDRS],
428         mtubuf1[MAXIPADDRS];
429     afs_uint32 addrbuf2[MAXIPADDRS], maskbuf2[MAXIPADDRS],
430         mtubuf2[MAXIPADDRS];
431     int nAddrs1 = 0;
432     afs_uint32 nAddrs2 = 0;
433     int code, i;
434
435     nAddrs1 =
436         afsconf_ParseNetInfoFile(addrbuf1, maskbuf1, mtubuf1, MAXIPADDRS,
437                                  reason, niFileName);
438     code =
439         parseNetRestrictFile_int(addrbuf2, maskbuf2, mtubuf2, MAXIPADDRS,
440                              &nAddrs2, reason, nrFileName, niFileName);
441     if ((nAddrs1 < 0) && (code)) {
442         /* both failed */
443         return -1;
444     } else if ((nAddrs1 > 0) && (code)) {
445         /* netinfo succeeded and netrestrict failed */
446         for (i = 0; ((i < nAddrs1) && (i < max)); i++) {
447             addrbuf[i] = addrbuf1[i];
448             if (maskbuf) {
449                 maskbuf[i] = maskbuf1[i];
450                 mtubuf[i] = mtubuf1[i];
451             }
452         }
453         return i;
454     } else if ((!code) && (nAddrs1 < 0)) {
455         /* netrestrict succeeded and netinfo failed */
456         for (i = 0; ((i < nAddrs2) && (i < max)); i++) {
457             addrbuf[i] = addrbuf2[i];
458             if (maskbuf) {
459                 maskbuf[i] = maskbuf2[i];
460                 mtubuf[i] = mtubuf2[i];
461             }
462         }
463         return i;
464     } else if ((!code) && (nAddrs1 >= 0)) {
465         /* both succeeded */
466         /* take the intersection of addrbuf1 and addrbuf2 */
467         code =
468             filterAddrs(addrbuf1, addrbuf2, maskbuf1, maskbuf2, mtubuf1,
469                         mtubuf2, nAddrs1, nAddrs2);
470         for (i = 0; ((i < code) && (i < max)); i++) {
471             addrbuf[i] = addrbuf1[i];
472             if (maskbuf) {
473                 maskbuf[i] = maskbuf1[i];
474                 mtubuf[i] = mtubuf1[i];
475             }
476         }
477         return i;
478     }
479     return 0;
480 }