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