servxcheck.c

Go to the documentation of this file.
00001 /*      servxcheck() - Service access check.            Author: Kees J. Bot
00002  *                                                              8 Jan 1997
00003  */
00004 #define nil 0
00005 #define ioctl _ioctl
00006 #define open _open
00007 #define write _write
00008 #define close _close
00009 #include <sys/types.h>
00010 #include <stdio.h>
00011 #include <stdlib.h>
00012 #include <errno.h>
00013 #include <string.h>
00014 #include <fcntl.h>
00015 #include <unistd.h>
00016 #include <time.h>
00017 #include <sys/ioctl.h>
00018 #include <net/hton.h>
00019 #include <net/gen/in.h>
00020 #include <net/gen/tcp.h>
00021 #include <net/gen/tcp_io.h>
00022 #include <net/gen/inet.h>
00023 #include <net/gen/socket.h>
00024 #include <net/gen/netdb.h>
00025 
00026 /* Default service access file. */
00027 static const char *path_servacces = _PATH_SERVACCES;
00028 
00029 #define WLEN    256
00030 
00031 static int getword(FILE *fp, char *word)
00032 /* Read a word from the file open by 'fp', skip whitespace and comments.
00033  * Colon and semicolon are returned as a one character "word".  Returns
00034  * word[0] or EOF.
00035  */
00036 {
00037     int c;
00038     char *pw;
00039     int wc;
00040 
00041     wc= 0;
00042     for (;;) {
00043         if ((c= getc(fp)) == EOF) return EOF;
00044         if (c == '#') { wc= 1; continue; }
00045         if (c == '\n') { wc= 0; continue; }
00046         if (wc) continue;
00047         if (c <= ' ') continue;
00048         break;
00049     }
00050 
00051     pw= word;
00052     if (c == ':' || c == ';') {
00053             *pw++ = c;
00054     } else {
00055         do {
00056             if (pw < word + WLEN-1) *pw++ = c;
00057             c= getc(fp);
00058         } while (c != EOF && c > ' ' && c != ':' && c != ';');
00059         if (c != EOF) ungetc(c, fp);
00060     }
00061     *pw= 0;
00062     return word[0];
00063 }
00064 
00065 static int netspec(char *word, ipaddr_t *addr, ipaddr_t *mask)
00066 /* Try to interpret 'word' as an network spec, e.g. 172.16.102.64/27. */
00067 {
00068     char *slash;
00069     int r;
00070     static char S32[]= "/32";
00071 
00072     if (*word == 0) return 0;
00073 
00074     if ((slash= strchr(word, '/')) == NULL) slash= S32;
00075 
00076     *slash= 0;
00077     r= inet_aton(word, addr);
00078     *slash++= '/';
00079     if (!r) return 0;
00080 
00081     r= 0;
00082     while ((*slash - '0') < 10u) {
00083         r= 10*r + (*slash++ - '0');
00084         if (r > 32) return 0;
00085     }
00086     if (*slash != 0 || slash[-1] == '/') return 0;
00087     *mask= htonl(r == 0 ? 0L : (0xFFFFFFFFUL >> (32 - r)) << (32 - r));
00088     return 1;
00089 }
00090 
00091 static int match(const char *word, const char *pattern)
00092 /* Match word onto a pattern.  Pattern may contain the * wildcard. */
00093 {
00094     unsigned cw, cp;
00095 #define lc(c, d) ((((c)= (d)) - 'A') <= ('Z' - 'A') ? (c)+= ('a' - 'A') : 0)
00096 
00097     for (;;) {
00098         lc(cw, *word);
00099         lc(cp, *pattern);
00100 
00101         if (cp == '*') {
00102             do pattern++; while (*pattern == '*');
00103             lc(cp, *pattern);
00104             if (cp == 0) return 1;
00105 
00106             while (cw != 0) {
00107                 if (cw == cp && match(word+1, pattern+1)) return 1;
00108                 word++;
00109                 lc(cw, *word);
00110             }
00111             return 0;
00112         } else
00113         if (cw == 0 || cp == 0) {
00114             return cw == cp;
00115         } else
00116         if (cw == cp) {
00117             word++;
00118             pattern++;
00119         } else {
00120             return 0;
00121         }
00122     }
00123 #undef lc
00124 }
00125 
00126 static int get_name(ipaddr_t addr, char *name)
00127 /* Do a reverse lookup on the remote IP address followed by a forward lookup
00128  * to check if the host has that address.  Return true if this is so, return
00129  * either the true name or the ascii IP address in name[].
00130  */
00131 {
00132     struct hostent *he;
00133     int i;
00134 
00135     he= gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
00136     if (he != NULL) {
00137         strcpy(name, he->h_name);
00138         he= gethostbyname(name);
00139 
00140         if (he != NULL && he->h_addrtype == AF_INET) {
00141             for (i= 0; he->h_addr_list[i] != NULL; i++) {
00142                 if (memcmp(he->h_addr_list[i], &addr, sizeof(addr)) == 0) {
00143                     strcpy(name, he->h_name);
00144                     return 1;
00145                 }
00146             }
00147         }
00148     }
00149     strcpy(name, inet_ntoa(addr));
00150     return 0;
00151 }
00152 
00153 /* "state" and "log" flags, made to be bitwise comparable. */
00154 #define DEFFAIL          0x01
00155 #define FAIL            (0x02 | DEFFAIL)
00156 #define PASS             0x04
00157 
00158 int servxcheck(ipaddr_t peer, const char *service,
00159                 void (*logf)(int pass, const char *name))
00160 {
00161     FILE *fp;
00162     char word[WLEN];
00163     char name[WLEN];
00164     int c;
00165     int got_name, slist, seen, explicit, state, log;
00166     ipaddr_t addr, mask;
00167 
00168     /* Localhost? */
00169     if ((peer & HTONL(0xFF000000)) == HTONL(0x7F000000)) return 1;
00170 
00171     if ((fp= fopen(path_servacces, "r")) == nil) {
00172         /* Succeed on error, fail if simply nonexistent. */
00173         return (errno != ENOENT);
00174     }
00175 
00176     slist= 1;           /* Services list (before the colon.) */
00177     seen= 0;            /* Given service not yet seen. */
00178     explicit= 0;        /* Service mentioned explicitly. */
00179     got_name= -1;       /* No reverse lookup done yet. */
00180     log= FAIL;          /* By default log failures only. */
00181     state= DEFFAIL;     /* Access denied until we know better. */
00182 
00183     while ((c= getword(fp, word)) != EOF) {
00184         if (c == ':') {
00185             slist= 0;           /* Switch to access list. */
00186         } else
00187         if (c == ';') {
00188             slist= 1;           /* Back to list of services. */
00189             seen= 0;
00190         } else
00191         if (slist) {
00192             /* Traverse services list. */
00193 
00194             if (match(service, word)) {
00195                 /* Service has been spotted! */
00196                 if (match(word, service)) {
00197                     /* Service mentioned without wildcards. */
00198                     seen= explicit= 1;
00199                 } else {
00200                     /* Matched by a wildcard. */
00201                     if (!explicit) seen= 1;
00202                 }
00203             }
00204         } else {
00205             /* Traverse access list. */
00206 
00207             if (c == 'l' && strcmp(word, "log") == 0) {
00208                 if (seen) {
00209                     /* Log failures and successes. */
00210                     log= FAIL|PASS;
00211                 }
00212                 continue;
00213             }
00214 
00215             if (c != '-' && c != '+') {
00216                 if (logf == nil) {
00217                     fprintf(stderr, "%s: strange check word '%s'\n",
00218                         path_servacces, word);
00219                 }
00220                 continue;
00221             }
00222 
00223             if (seen) {
00224                 if (state == DEFFAIL) {
00225                     /* First check determines the default. */
00226                     state= c == '+' ? FAIL : PASS;
00227                 }
00228 
00229                 if ((state == PASS) == (c == '+')) {
00230                     /* This check won't change state. */
00231                 } else
00232                 if (word[1] == 0) {
00233                     /* Lone + or - allows all or none. */
00234                     state= c == '-' ? FAIL : PASS;
00235                 } else
00236                 if (netspec(word+1, &addr, &mask)) {
00237                     /* Remote host is on the specified network? */
00238                     if (((peer ^ addr) & mask) == 0) {
00239                         state= c == '-' ? FAIL : PASS;
00240                     }
00241                 } else {
00242                     /* Name check. */
00243                     if (got_name == -1) {
00244                         got_name= get_name(peer, name);
00245                     }
00246 
00247                     /* Remote host name matches the word? */
00248                     if (!got_name) {
00249                         state= FAIL;
00250                     } else
00251                     if (match(name, word+1)) {
00252                         state= c == '-' ? FAIL : PASS;
00253                     }
00254                 }
00255             }
00256         }
00257     }
00258     fclose(fp);
00259 
00260     if ((log & state) != 0) {
00261         /* Log the result of the check. */
00262         if (got_name == -1) (void) get_name(peer, name);
00263 
00264         if (logf != nil) {
00265             (*logf)(state == PASS, name);
00266         } else {
00267             int lfd;
00268             char line[128+WLEN];
00269             time_t t;
00270             struct tm *tm;
00271             char month[][4]= {
00272                 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
00273                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
00274             };
00275 
00276             if ((lfd= open("/usr/adm/log", O_WRONLY|O_APPEND)) != -1) {
00277                 time(&t);
00278                 tm= localtime(&t);
00279                 sprintf(line, "%s %02d %02d:%02d:%02d service '%s' %s to %s\n",
00280                     month[tm->tm_mon],
00281                     tm->tm_mday,
00282                     tm->tm_hour, tm->tm_min, tm->tm_sec,
00283                     service,
00284                     state == PASS ? "granted" : "denied",
00285                     name);
00286                 (void) write(lfd, line, strlen(line));
00287                 close(lfd);
00288             }
00289         }
00290     }
00291     return state == PASS;
00292 }
00293 
00294 char *servxfile(const char *file)
00295 /* Specify a file to use for the access checks other than the default.  Return
00296  * the old path.
00297  */
00298 {
00299     const char *oldpath= path_servacces;
00300     path_servacces= file;
00301     return (char *) oldpath;    /* (avoid const poisoning) */
00302 }

Generated on Fri Apr 14 22:57:26 2006 for minix by  doxygen 1.4.6