/* * ntp_restrict.c - find out what restrictions this host is running under */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "ntpd.h" #include "ntp_if.h" #include "ntp_stdlib.h" /* * This code keeps a simple address-and-mask list of hosts we want * to place restrictions on (or remove them from). The restrictions * are implemented as a set of flags which tell you what the host * can't do. There is a subroutine entry to return the flags. The * list is kept sorted to reduce the average number of comparisons * and make sure you get the set of restrictions most specific to * the address. * * The algorithm is that, when looking up a host, it is first assumed * that the default set of restrictions will apply. It then searches * down through the list. Whenever it finds a match it adopts the match's * flags instead. When you hit the point where the sorted address is * greater than the target, you return with the last set of flags you * found. Because of the ordering of the list, the most specific match * will provide the final set of flags. * * This was originally intended to restrict you from sync'ing to your * own broadcasts when you are doing that, by restricting yourself * from your own interfaces. It was also thought it would sometimes * be useful to keep a misbehaving host or two from abusing your primary * clock. It has been expanded, however, to suit the needs of those * with more restrictive access policies. */ /* * Memory allocation parameters. We allocate INITRESLIST entries * initially, and add INCRESLIST entries to the free list whenever * we run out. */ #define INITRESLIST 10 #define INCRESLIST 5 /* * The restriction list */ struct restrictlist *restrictlist; static int restrictcount; /* count of entries in the restriction list */ /* * The free list and associated counters. Also some uninteresting * stat counters. */ static struct restrictlist *resfree; static int numresfree; /* number of structures on free list */ static u_long res_calls; static u_long res_found; static u_long res_not_found; /* static u_long res_timereset; */ /* * Parameters of the RES_LIMITED restriction option. * client_limit is the number of hosts allowed per source net * client_limit_period is the number of seconds after which an entry * is no longer considered for client limit determination */ u_long client_limit; u_long client_limit_period; /* * count number of restriction entries referring to RES_LIMITED * controls activation/deactivation of monitoring * (with respect to RES_LIMITED control) */ static u_long res_limited_refcnt; /* * Our initial allocation of list entries. */ static struct restrictlist resinit[INITRESLIST]; /* * init_restrict - initialize the restriction data structures */ void init_restrict(void) { register int i; char bp[80]; /* * Zero the list and put all but one on the free list */ resfree = 0; memset((char *)resinit, 0, sizeof resinit); for (i = 1; i < INITRESLIST; i++) { resinit[i].next = resfree; resfree = &resinit[i]; } numresfree = INITRESLIST-1; /* * Put the remaining item at the head of the * list as our default entry. Everything in here * should be zero for now. */ resinit[0].addr = htonl(INADDR_ANY); resinit[0].mask = 0; restrictlist = &resinit[0]; restrictcount = 1; /* * fix up stat counters */ res_calls = 0; res_found = 0; res_not_found = 0; /* res_timereset = 0; */ /* * set default values for RES_LIMIT functionality */ client_limit = 3; client_limit_period = 3600; res_limited_refcnt = 0; sprintf(bp, "client_limit=%ld", client_limit); set_sys_var(bp, strlen(bp)+1, RO); sprintf(bp, "client_limit_period=%ld", client_limit_period); set_sys_var(bp, strlen(bp)+1, RO); } /* * restrictions - return restrictions for this host */ int restrictions( struct sockaddr_in *srcadr ) { register struct restrictlist *rl; register struct restrictlist *match; register u_int32 hostaddr; register int isntpport; res_calls++; /* * We need the host address in host order. Also need to know * whether this is from the ntp port or not. */ hostaddr = SRCADR(srcadr); isntpport = (SRCPORT(srcadr) == NTP_PORT); /* * Ignore any packets with a multicast source address * (this should be done early in the receive process, later!) */ if (IN_CLASSD(ntohl(srcadr->sin_addr.s_addr))) return (int)RES_IGNORE; /* * Set match to first entry, which is default entry. Work our * way down from there. */ match = restrictlist; for (rl = match->next; rl != 0 && rl->addr <= hostaddr; rl = rl->next) if ((hostaddr & rl->mask) == rl->addr) { if ((rl->mflags & RESM_NTPONLY) && !isntpport) continue; match = rl; } match->count++; if (match == restrictlist) res_not_found++; else res_found++; /* * The following implements limiting the number of clients * accepted from a given network. The notion of "same network" * is determined by the mask and addr fields of the restrict * list entry. The monitor mechanism has to be enabled for * collecting info on current clients. * * The policy is as follows: * - take the list of clients recorded * from the given "network" seen within the last * client_limit_period seconds * - if there are at most client_limit entries: * --> access allowed * - otherwise sort by time first seen * - current client among the first client_limit seen * hosts? * if yes: access allowed * else: eccess denied */ if (match->flags & RES_LIMITED) { int lcnt; struct mon_data *md, *this_client; #ifdef DEBUG if (debug > 2) printf("limited clients check: %ld clients, period %ld seconds, net is 0x%lX\n", client_limit, client_limit_period, (u_long)netof(hostaddr)); #endif /*DEBUG*/ if (mon_enabled == MON_OFF) { #ifdef DEBUG if (debug > 4) printf("no limit - monitoring is off\n"); #endif return (int)(match->flags & ~RES_LIMITED); } /* * How nice, MRU list provides our current client as the * first entry in the list. * Monitoring was verified to be active above, thus we * know an entry for our client must exist, or some * brain dead set the memory limit for mon entries to ZERO!!! */ this_client = mon_mru_list.mru_next; for (md = mon_fifo_list.fifo_next,lcnt = 0; md != &mon_fifo_list; md = md->fifo_next) { if ((current_time - md->lasttime) > client_limit_period) { #ifdef DEBUG if (debug > 5) printf("checking: %s: ignore: too old: %ld\n", numtoa(md->rmtadr), current_time - md->lasttime); #endif continue; } if (md->mode == MODE_BROADCAST || md->mode == MODE_CONTROL || md->mode == MODE_PRIVATE) { #ifdef DEBUG if (debug > 5) printf("checking: %s: ignore mode %d\n", numtoa(md->rmtadr), md->mode); #endif continue; } if (netof(md->rmtadr) != netof(hostaddr)) { #ifdef DEBUG if (debug > 5) printf("checking: %s: different net 0x%lX\n", numtoa(md->rmtadr), (u_long)netof(md->rmtadr)); #endif continue; } lcnt++; if (lcnt > (int) client_limit || md->rmtadr == hostaddr) { #ifdef DEBUG if (debug > 5) printf("considering %s: found host\n", numtoa(md->rmtadr)); #endif break; } #ifdef DEBUG else { if (debug > 5) printf("considering %s: same net\n", numtoa(md->rmtadr)); } #endif } #ifdef DEBUG if (debug > 4) printf("this one is rank %d in list, limit is %lu: %s\n", lcnt, client_limit, (lcnt <= (int) client_limit) ? "ALLOW" : "REJECT"); #endif if (lcnt <= (int) client_limit) { this_client->lastdrop = 0; return (int)(match->flags & ~RES_LIMITED); } else { this_client->lastdrop = current_time; } } return (int)match->flags; } /* * hack_restrict - add/subtract/manipulate entries on the restrict list */ void hack_restrict( int op, struct sockaddr_in *resaddr, struct sockaddr_in *resmask, int mflags, int flags ) { register u_int32 addr; register u_int32 mask; register struct restrictlist *rl; register struct restrictlist *rlprev; int i; /* * Get address and mask in host byte order */ addr = SRCADR(resaddr); mask = SRCADR(resmask); addr &= mask; /* make sure low bits are zero */ /* * If this is the default address, point at first on list. Else * go searching for it. */ if (addr == htonl(INADDR_ANY)) { rlprev = 0; rl = restrictlist; } else { rlprev = restrictlist; rl = rlprev->next; while (rl != 0) { if (rl->addr > addr) { rl = 0; break; } else if (rl->addr == addr) { if (rl->mask == mask) { if ((mflags & RESM_NTPONLY) == (rl->mflags & RESM_NTPONLY)) break; /* exact match */ if (!(mflags & RESM_NTPONLY)) { /* * No flag fits before flag */ rl = 0; break; } /* continue on */ } else if (rl->mask > mask) { rl = 0; break; } } rlprev = rl; rl = rl->next; } } /* * In case the above wasn't clear :-), either rl now points * at the entry this call refers to, or rl is zero and rlprev * points to the entry prior to where this one should go in * the sort. */ /* * Switch based on operation */ switch (op) { case RESTRICT_FLAGS: /* * Here we add bits to the flags. If this is a new * restriction add it. */ if (rl == 0) { if (numresfree == 0) { rl = (struct restrictlist *) emalloc( INCRESLIST*sizeof(struct restrictlist)); memset((char *)rl, 0, INCRESLIST*sizeof(struct restrictlist)); for (i = 0; i < INCRESLIST; i++) { rl->next = resfree; resfree = rl; rl++; } numresfree = INCRESLIST; } rl = resfree; resfree = rl->next; numresfree--; rl->addr = addr; rl->mask = mask; rl->mflags = (u_short)mflags; rl->next = rlprev->next; rlprev->next = rl; restrictcount++; } if ((rl->flags ^ (u_short)flags) & RES_LIMITED) { res_limited_refcnt++; mon_start(MON_RES); /* ensure data gets collected */ } rl->flags |= (u_short)flags; break; case RESTRICT_UNFLAG: /* * Remove some bits from the flags. If we didn't * find this one, just return. */ if (rl != 0) { if ((rl->flags ^ (u_short)flags) & RES_LIMITED) { res_limited_refcnt--; if (res_limited_refcnt == 0) mon_stop(MON_RES); } rl->flags &= (u_short)~flags; } break; case RESTRICT_REMOVE: /* * Remove an entry from the table entirely if we found one. * Don't remove the default entry and don't remove an * interface entry. */ if (rl != 0 && rl->addr != htonl(INADDR_ANY) && !(rl->mflags & RESM_INTERFACE)) { rlprev->next = rl->next; restrictcount--; if (rl->flags & RES_LIMITED) { res_limited_refcnt--; if (res_limited_refcnt == 0) mon_stop(MON_RES); } memset((char *)rl, 0, sizeof(struct restrictlist)); rl->next = resfree; resfree = rl; numresfree++; } break; default: /* Oh, well */ break; } /* done! */ }