#include #include #include #include #include #include #include #include #include #include #include #include "getline.h" #include "acl.h" static char **aclv = 0; static int acla = 0; static int acln = 0; static int acl_verbose = 0; #define Cisspace(x) isspace((unsigned char)(x)) static unsigned long int v4_masks[33] = { 0x00000000, 0x80000000, 0xc0000000, 0xe0000000, 0xf0000000, 0xf8000000, 0xfc000000, 0xfe000000, 0xff000000, 0xff800000, 0xffc00000, 0xffe00000, 0xfff00000, 0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000, 0xffff8000, 0xffffc000, 0xffffe000, 0xfffff000, 0xfffff800, 0xfffffc00, 0xfffffe00, 0xffffff00, 0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0, 0xfffffff8, 0xfffffffc, 0xfffffffe, 0xffffffff }; static unsigned long int v6_masks[9] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; static int v6_match(const void *a1, const void *a2, unsigned int w) { if (bcmp(a1,a2,w/8)) return(0); if ( (w % 8) && ( ( ((const unsigned char *)a1)[w/8] ^ ((const unsigned char *)a2)[w/8] ) & v6_masks[w%8] ) ) return(0); return(1); } static int choose_action(const char *s, int l) { switch (l) { case 2: if (!bcmp(s,"no",2)) return(0); break; case 3: if (!bcmp(s,"yes",3)) return(1); break; case 4: if (!bcmp(s,"deny",4)) return(0); break; case 6: if (!bcmp(s,"accept",6)) return(1); break; } return(-1); } static int acl_chk(const char *path, const struct sockaddr *sa, int salen) { int fd; struct stat stb; int onmatch; int decision; fd = open(path,O_RDONLY,0); if (fd < 0) { if (acl_verbose) printf("open %s failed: %s\n",path,strerror(errno)); return(-1); } if (fstat(fd,&stb) < 0) { if (acl_verbose) printf("fstat %s failed: %s\n",path,strerror(errno)); close(fd); return(-1); } switch (stb.st_mode & S_IFMT) { case S_IFREG: { FILE *f; LINEGETTER *lg; char *line; if (acl_verbose) printf("%s is a plain file\n",path); f = fdopen(fd,"r"); lg = lg_init(f); decision = -1; while ((line = lg_getline(lg))) { char *lp; char *ip0; char *ip1; char *act0; char *act1; char *slash; char *ipend; char *ep; int e; long int width; struct addrinfo hints; struct addrinfo *ai; char *nn; int match; if (acl_verbose) printf("%s line: %s\n",path,line); for (lp=line;(*lp)&&Cisspace(*lp);lp++) ; switch <"parse"> (*lp) { case '\0': case ';': case '#': if (acl_verbose) printf(" line is a comment\n"); break; default: ip0 = lp; for (;(*lp)&&!Cisspace(*lp);lp++) ; if (! *lp) { if (acl_verbose) printf(" line ends after first field\n"); break; } ip1 = lp; for (;(*lp)&&Cisspace(*lp);lp++) ; if (! *lp) { if (acl_verbose) printf(" line ends after whitespace after first field\n"); break; } act0 = lp; for (;(*lp)&&!Cisspace(*lp);lp++) ; act1 = lp; onmatch = choose_action(act0,act1-act0); if (onmatch < 0) { if (acl_verbose) printf(" bad action verb %.*s\n",(int)(act1-act0),act0); break <"parse">; } if (acl_verbose) printf(" onmatch %d\n",onmatch); slash = memchr(ip0,'/',ip1-ip0); if (slash) { width = strtol(slash+1,&ep,0); if ( (ep == slash+1) || (ep != ip1) || (width < 0) || (width > 128) ) break <"parse">; ipend = slash; if (acl_verbose) printf(" CIDR, width %d IP %.*s\n",(int)width,(int)(ipend-ip0),ip0); } else { switch (sa->sa_family) { case AF_INET: width = 32; break; case AF_INET6: width = 128; break; default: break <"parse">; break; } ipend = ip1; if (acl_verbose) printf(" host, width %d IP %.*s\n",(int)width,(int)(ipend-ip0),ip0); } nn = malloc((ipend-ip0)+1); bcopy(ip0,nn,ipend-ip0); nn[ipend-ip0] = '\0'; hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; e = getaddrinfo(nn,0,&hints,&ai); free(nn); if (e) { if (acl_verbose) printf(" getaddrinfo failed (%s)\n",gai_strerror(e)); break <"parse">; } if (! ai) { if (acl_verbose) printf(" getaddrinfo succeeded but no addresses\n"); break <"parse">; } match = 0; if (ai->ai_addr->sa_family == sa->sa_family) { switch (sa->sa_family) { case AF_INET: if (acl_verbose) printf(" IPv4, %08lx vs %08lx mask %08lx\n", (unsigned long int)ntohl(((const struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr), (unsigned long int)ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr), v4_masks[width] ); if ( (width <= 32) && !(( ntohl(((const struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr) ^ ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr) ) & v4_masks[width]) ) { match = 1; if (acl_verbose) printf(" match!\n"); } break; case AF_INET6: if (acl_verbose) { printf(" IPv6, %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x vs %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x width %d\n", #define X ((const unsigned char *)&((const struct sockaddr_in6 *)ai->ai_addr)->sin6_addr) X[0],X[1],X[2],X[3],X[4],X[5],X[6],X[7],X[8],X[9],X[10],X[11],X[12],X[13],X[14],X[15], #undef X #define X ((const unsigned char *)&((const struct sockaddr_in6 *)sa)->sin6_addr) X[0],X[1],X[2],X[3],X[4],X[5],X[6],X[7],X[8],X[9],X[10],X[11],X[12],X[13],X[14],X[15], #undef X (int)width); } if ( (width <= 128) && v6_match( &((const struct sockaddr_in6 *)ai->ai_addr)->sin6_addr, &((const struct sockaddr_in6 *)sa)->sin6_addr, width ) ) { match = 1; if (acl_verbose) printf(" match!\n"); } break; } } freeaddrinfo(ai); if (match) { if (acl_verbose) printf(" setting decision to %d\n",onmatch); decision = onmatch; } break; } if (decision >= 0) { if (acl_verbose) printf(" decision is %d\n",decision); break; } } lg_done(lg); fclose(f); if (acl_verbose) printf(" returning %d\n",decision); return(decision); } break; case S_IFDIR: { char hn[NI_MAXHOST]; int e; if (acl_verbose) printf("%s is a directory\n",path); e = getnameinfo(sa,salen,&hn[0],NI_MAXHOST,0,0,NI_NUMERICHOST|NI_NUMERICSERV); if (! e) { char *t; char action[64]; int alen; asprintf(&t,"%s/%s",path,&hn[0]); if (acl_verbose) printf(" checking %s\n",t); alen = readlink(t,&action[0],sizeof(action)); if (acl_verbose) { if (alen < 0) { printf(" readlink failed: %s\n",strerror(errno)); } else { printf(" readlink worked: %.*s\n",alen,&action[0]); } } free(t); decision = choose_action(&action[0],alen); if (acl_verbose) printf(" set decision to %d\n",decision); } else { decision = -1; } close(fd); if (acl_verbose) printf(" returning %d\n",decision); return(decision); } break; } close(fd); if (acl_verbose) printf(" unrecognized type, returning %d\n",-1); return(-1); } void acl_add_list(const char *path) { int i; for (i=0;i= acla) aclv = realloc(aclv,(acla=acln+8)*sizeof(*aclv)); aclv[acln++] = strdup(path); } int acl_check(const struct sockaddr *sa, int salen) { int i; int rv; for (i=0;i= 0) return(rv); } return(1); } void acl_test(const char *addr) { struct addrinfo hints; struct addrinfo *ai; int e; hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; e = getaddrinfo(addr,0,&hints,&ai); if (e) { printf("%s: %s\n",addr,gai_strerror(e)); } else if (! ai) { printf("%s: lookup succeeded but with no addresses\n",addr); } else { acl_verbose = 1; if (acl_check(ai->ai_addr,ai->ai_addrlen)) { printf("Allow\n"); } else { printf("Deny\n"); } } exit(0); }