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