Home | History | Annotate | Download | only in src
      1 /*-
      2  * Copyright (c) 1996 - 2001 Brian Somers <brian (at) Awfulhak.org>
      3  *          based on work by Toshiharu OHNO <tony-o (at) iij.ad.jp>
      4  *                           Internet Initiative Japan, Inc (IIJ)
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  *
     28  * $FreeBSD: src/usr.sbin/ppp/systems.c,v 1.68.26.1 2010/12/21 17:10:29 kensmith Exp $
     29  */
     30 
     31 #include <sys/param.h>
     32 
     33 #include <ctype.h>
     34 #include <pwd.h>
     35 #include <stdio.h>
     36 #include <stdlib.h>
     37 #include <string.h>
     38 #include <termios.h>
     39 
     40 #include "defs.h"
     41 #include "command.h"
     42 #include "log.h"
     43 #include "id.h"
     44 #include "systems.h"
     45 
     46 #define issep(ch) ((ch) == ' ' || (ch) == '\t')
     47 
     48 FILE *
     49 OpenSecret(const char *file)
     50 {
     51   FILE *fp;
     52   char line[100];
     53 
     54   snprintf(line, sizeof line, "%s/%s", PPP_CONFDIR, file);
     55   fp = ID0fopen(line, "r");
     56   if (fp == NULL)
     57     log_Printf(LogWARN, "OpenSecret: Can't open %s.\n", line);
     58   return (fp);
     59 }
     60 
     61 void
     62 CloseSecret(FILE *fp)
     63 {
     64   fclose(fp);
     65 }
     66 
     67 /* Move string from ``from'' to ``to'', interpreting ``~'' and $.... */
     68 const char *
     69 InterpretArg(const char *from, char *to)
     70 {
     71   char *ptr, *startto, *endto;
     72   struct passwd *pwd;
     73   int instring;
     74   size_t len;
     75   const char *env;
     76 
     77   instring = 0;
     78   startto = to;
     79   endto = to + LINE_LEN - 1;
     80 
     81   while(issep(*from))
     82     from++;
     83 
     84   while (*from != '\0') {
     85     switch (*from) {
     86       case '"':
     87         instring = !instring;
     88         *to++ = *from++;
     89         break;
     90       case '\\':
     91         switch (*++from) {
     92           case '$':
     93           case '~':
     94             break;		/* Swallow the escapes */
     95 
     96           default:
     97             *to++ = '\\';	/* Pass the escapes on, maybe skipping \# */
     98             break;
     99         }
    100         *to++ = *from++;
    101         break;
    102       case '$':
    103         if (from[1] == '$') {
    104           *to = '\0';	/* For an empty var name below */
    105           from += 2;
    106         } else if (from[1] == '{') {
    107           ptr = strchr(from+2, '}');
    108           if (ptr) {
    109             len = ptr - from - 2;
    110             if (endto - to < (int)len )
    111               len = endto - to;
    112             if (len) {
    113               strncpy(to, from+2, len);
    114               to[len] = '\0';
    115               from = ptr+1;
    116             } else {
    117               *to++ = *from++;
    118               continue;
    119             }
    120           } else {
    121             *to++ = *from++;
    122             continue;
    123           }
    124         } else {
    125           ptr = to;
    126           for (from++; (isalnum(*from) || *from == '_') && ptr < endto; from++)
    127             *ptr++ = *from;
    128           *ptr = '\0';
    129         }
    130         if (*to == '\0')
    131           *to++ = '$';
    132         else if ((env = getenv(to)) != NULL) {
    133           strncpy(to, env, endto - to);
    134           *endto = '\0';
    135           to += strlen(to);
    136         }
    137         break;
    138 
    139       case '~':
    140         ptr = strchr(++from, '/');
    141         len = ptr ? (size_t)(ptr - from) : strlen(from);
    142         if (len == 0)
    143           pwd = getpwuid(ID0realuid());
    144         else {
    145           strncpy(to, from, len);
    146           to[len] = '\0';
    147           pwd = getpwnam(to);
    148         }
    149         if (pwd == NULL)
    150           *to++ = '~';
    151         else {
    152           strncpy(to, pwd->pw_dir, endto - to);
    153           *endto = '\0';
    154           to += strlen(to);
    155           from += len;
    156         }
    157         endpwent();
    158         break;
    159 
    160       default:
    161         *to++ = *from++;
    162         break;
    163     }
    164   }
    165 
    166   while (to > startto) {
    167     to--;
    168     if (!issep(*to)) {
    169       to++;
    170       break;
    171     }
    172   }
    173   *to = '\0';
    174 
    175   return from;
    176 }
    177 
    178 #define CTRL_UNKNOWN (0)
    179 #define CTRL_INCLUDE (1)
    180 
    181 static int
    182 DecodeCtrlCommand(char *line, char *arg)
    183 {
    184   const char *end;
    185 
    186   if (!strncasecmp(line, "include", 7) && issep(line[7])) {
    187     end = InterpretArg(line+8, arg);
    188     if (*end && *end != '#')
    189       log_Printf(LogWARN, "usage: !include filename\n");
    190     else
    191       return CTRL_INCLUDE;
    192   }
    193   return CTRL_UNKNOWN;
    194 }
    195 
    196 /*
    197  * Initialised in system_IsValid(), set in ReadSystem(),
    198  * used by system_IsValid()
    199  */
    200 static int modeok;
    201 static int userok;
    202 static int modereq;
    203 
    204 int
    205 AllowUsers(struct cmdargs const *arg)
    206 {
    207   /* arg->bundle may be NULL (see system_IsValid()) ! */
    208   int f;
    209   struct passwd *pwd;
    210 
    211   if (userok == -1)
    212     userok = 0;
    213 
    214   pwd = getpwuid(ID0realuid());
    215   if (pwd != NULL)
    216     for (f = arg->argn; f < arg->argc; f++)
    217       if (!strcmp("*", arg->argv[f]) || !strcmp(pwd->pw_name, arg->argv[f])) {
    218         userok = 1;
    219         break;
    220       }
    221   endpwent();
    222 
    223   return 0;
    224 }
    225 
    226 int
    227 AllowModes(struct cmdargs const *arg)
    228 {
    229   /* arg->bundle may be NULL (see system_IsValid()) ! */
    230   int f, mode, allowed;
    231 
    232   allowed = 0;
    233   for (f = arg->argn; f < arg->argc; f++) {
    234     mode = Nam2mode(arg->argv[f]);
    235     if (mode == PHYS_NONE || mode == PHYS_ALL)
    236       log_Printf(LogWARN, "allow modes: %s: Invalid mode\n", arg->argv[f]);
    237     else
    238       allowed |= mode;
    239   }
    240 
    241   modeok = modereq & allowed ? 1 : 0;
    242   return 0;
    243 }
    244 
    245 static char *
    246 strip(char *line)
    247 {
    248   int len;
    249 
    250   len = strlen(line);
    251   while (len && (line[len-1] == '\n' || line[len-1] == '\r' ||
    252                  issep(line[len-1])))
    253     line[--len] = '\0';
    254 
    255   while (issep(*line))
    256     line++;
    257 
    258   if (*line == '#')
    259     *line = '\0';
    260 
    261   return line;
    262 }
    263 
    264 static int
    265 xgets(char *buf, int buflen, FILE *fp)
    266 {
    267   int len, n;
    268 
    269   n = 0;
    270   while (fgets(buf, buflen-1, fp)) {
    271     n++;
    272     buf[buflen-1] = '\0';
    273     len = strlen(buf);
    274     while (len && (buf[len-1] == '\n' || buf[len-1] == '\r'))
    275       buf[--len] = '\0';
    276     if (len && buf[len-1] == '\\') {
    277       buf += len - 1;
    278       buflen -= len - 1;
    279       if (!buflen)        /* No buffer space */
    280         break;
    281     } else
    282       break;
    283   }
    284   return n;
    285 }
    286 
    287 /* Values for ``how'' in ReadSystem */
    288 #define SYSTEM_EXISTS	1
    289 #define SYSTEM_VALIDATE	2
    290 #define SYSTEM_EXEC	3
    291 
    292 static char *
    293 GetLabel(char *line, const char *filename, int linenum)
    294 {
    295   char *argv[MAXARGS];
    296   int argc, len;
    297 
    298   argc = MakeArgs(line, argv, MAXARGS, PARSE_REDUCE);
    299 
    300   if (argc == 2 && !strcmp(argv[1], ":"))
    301     return argv[0];
    302 
    303   if (argc != 1 || (len = strlen(argv[0])) < 2 || argv[0][len-1] != ':') {
    304       log_Printf(LogWARN, "Bad label in %s (line %d) - missing colon\n",
    305                  filename, linenum);
    306       return NULL;
    307   }
    308   argv[0][len-1] = '\0';	/* Lose the ':' */
    309 
    310   return argv[0];
    311 }
    312 
    313 /* Returns -2 for ``file not found'' and -1 for ``label not found'' */
    314 
    315 static int
    316 ReadSystem(struct bundle *bundle, const char *name, const char *file,
    317            struct prompt *prompt, struct datalink *cx, int how)
    318 {
    319   FILE *fp;
    320   char *cp;
    321   int n, len;
    322   char line[LINE_LEN];
    323   char filename[PATH_MAX];
    324   int linenum;
    325   int argc;
    326   char *argv[MAXARGS];
    327   int allowcmd;
    328   int indent;
    329   char arg[LINE_LEN];
    330   struct prompt *op;
    331 
    332   if (*file == '/')
    333     snprintf(filename, sizeof filename, "%s", file);
    334   else
    335     snprintf(filename, sizeof filename, "%s/%s", PPP_CONFDIR, file);
    336   fp = ID0fopen(filename, "r");
    337   if (fp == NULL) {
    338     log_Printf(LogDEBUG, "ReadSystem: Can't open %s.\n", filename);
    339     return -2;
    340   }
    341   log_Printf(LogDEBUG, "ReadSystem: Checking %s (%s).\n", name, filename);
    342 
    343   linenum = 0;
    344   while ((n = xgets(line, sizeof line, fp))) {
    345     linenum += n;
    346     if (issep(*line))
    347       continue;
    348 
    349     cp = strip(line);
    350 
    351     switch (*cp) {
    352     case '\0':			/* empty/comment */
    353       break;
    354 
    355     case '!':
    356       switch (DecodeCtrlCommand(cp+1, arg)) {
    357       case CTRL_INCLUDE:
    358         log_Printf(LogCOMMAND, "%s: Including \"%s\"\n", filename, arg);
    359         n = ReadSystem(bundle, name, arg, prompt, cx, how);
    360         log_Printf(LogCOMMAND, "%s: Done include of \"%s\"\n", filename, arg);
    361         if (!n) {
    362           fclose(fp);
    363           return 0;	/* got it */
    364         }
    365         break;
    366       default:
    367         log_Printf(LogWARN, "%s: %s: Invalid command\n", filename, cp);
    368         break;
    369       }
    370       break;
    371 
    372     default:
    373       if ((cp = GetLabel(cp, filename, linenum)) == NULL)
    374         continue;
    375 
    376       if (strcmp(cp, name) == 0) {
    377         /* We're in business */
    378         if (how == SYSTEM_EXISTS) {
    379           fclose(fp);
    380 	  return 0;
    381 	}
    382 	while ((n = xgets(line, sizeof line, fp))) {
    383           linenum += n;
    384           indent = issep(*line);
    385           cp = strip(line);
    386 
    387           if (*cp == '\0')			/* empty / comment */
    388             continue;
    389 
    390           if (!indent) {			/* start of next section */
    391             if (*cp != '!' && how == SYSTEM_EXEC)
    392               cp = GetLabel(cp, filename, linenum);
    393             break;
    394           }
    395 
    396           len = strlen(cp);
    397           if ((argc = command_Expand_Interpret(cp, len, argv, cp - line)) < 0)
    398             log_Printf(LogWARN, "%s: %d: Syntax error\n", filename, linenum);
    399           else {
    400             allowcmd = argc > 0 && !strcasecmp(argv[0], "allow");
    401             if ((how != SYSTEM_EXEC && allowcmd) ||
    402                 (how == SYSTEM_EXEC && !allowcmd)) {
    403               /*
    404                * Disable any context so that warnings are given to everyone,
    405                * including syslog.
    406                */
    407               op = log_PromptContext;
    408               log_PromptContext = NULL;
    409 	      command_Run(bundle, argc, (char const *const *)argv, prompt,
    410                           name, cx);
    411               log_PromptContext = op;
    412             }
    413           }
    414         }
    415 
    416 	fclose(fp);  /* everything read - get out */
    417 	return 0;
    418       }
    419       break;
    420     }
    421   }
    422   fclose(fp);
    423   return -1;
    424 }
    425 
    426 const char *
    427 system_IsValid(const char *name, struct prompt *prompt, int mode)
    428 {
    429   /*
    430    * Note:  The ReadSystem() calls only result in calls to the Allow*
    431    * functions.  arg->bundle will be set to NULL for these commands !
    432    */
    433   int def, how, rs;
    434   int defuserok;
    435 
    436   def = !strcmp(name, "default");
    437   how = ID0realuid() == 0 ? SYSTEM_EXISTS : SYSTEM_VALIDATE;
    438   userok = -1;
    439   modeok = 1;
    440   modereq = mode;
    441 
    442   rs = ReadSystem(NULL, "default", CONFFILE, prompt, NULL, how);
    443 
    444   defuserok = userok;
    445   userok = -1;
    446 
    447   if (!def) {
    448     if (rs == -1)
    449       rs = 0;		/* we don't care that ``default'' doesn't exist */
    450 
    451     if (rs == 0)
    452       rs = ReadSystem(NULL, name, CONFFILE, prompt, NULL, how);
    453 
    454     if (rs == -1)
    455       return "Configuration label not found";
    456 
    457     if (rs == -2)
    458       return PPP_CONFDIR "/" CONFFILE " : File not found";
    459   }
    460 
    461   if (userok == -1)
    462     userok = defuserok;
    463 
    464   if (how == SYSTEM_EXISTS)
    465     userok = modeok = 1;
    466 
    467   if (!userok)
    468     return "User access denied";
    469 
    470   if (!modeok)
    471     return "Mode denied for this label";
    472 
    473   return NULL;
    474 }
    475 
    476 int
    477 system_Select(struct bundle *bundle, const char *name, const char *file,
    478              struct prompt *prompt, struct datalink *cx)
    479 {
    480   userok = modeok = 1;
    481   modereq = PHYS_ALL;
    482   return ReadSystem(bundle, name, file, prompt, cx, SYSTEM_EXEC);
    483 }
    484