systems.c revision 96582
1/*-
2 * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
3 *          based on work by Toshiharu OHNO <tony-o@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: head/usr.sbin/ppp/systems.c 96582 2002-05-14 12:55:39Z brian $
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
48FILE *
49OpenSecret(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
61void
62CloseSecret(FILE *fp)
63{
64  fclose(fp);
65}
66
67/* Move string from ``from'' to ``to'', interpreting ``~'' and $.... */
68const char *
69InterpretArg(const char *from, char *to)
70{
71  char *ptr, *startto, *endto;
72  struct passwd *pwd;
73  int len, instring;
74  const char *env;
75
76  instring = 0;
77  startto = to;
78  endto = to + LINE_LEN - 1;
79
80  while(issep(*from))
81    from++;
82
83  while (*from != '\0') {
84    switch (*from) {
85      case '"':
86        instring = !instring;
87        *to++ = *from++;
88        break;
89      case '\\':
90        switch (*++from) {
91          case '$':
92          case '~':
93            break;		/* Swallow the escapes */
94
95          default:
96            *to++ = '\\';	/* Pass the escapes on, maybe skipping \# */
97            break;
98        }
99        *to++ = *from++;
100        break;
101      case '$':
102        if (from[1] == '$') {
103          *to = '\0';	/* For an empty var name below */
104          from += 2;
105        } else if (from[1] == '{') {
106          ptr = strchr(from+2, '}');
107          if (ptr) {
108            len = ptr - from - 2;
109            if (endto - to < len )
110              len = endto - to;
111            if (len) {
112              strncpy(to, from+2, len);
113              to[len] = '\0';
114              from = ptr+1;
115            } else {
116              *to++ = *from++;
117              continue;
118            }
119          } else {
120            *to++ = *from++;
121            continue;
122          }
123        } else {
124          ptr = to;
125          for (from++; (isalnum(*from) || *from == '_') && ptr < endto; from++)
126            *ptr++ = *from;
127          *ptr = '\0';
128        }
129        if (*to == '\0')
130          *to++ = '$';
131        else if ((env = getenv(to)) != NULL) {
132          strncpy(to, env, endto - to);
133          *endto = '\0';
134          to += strlen(to);
135        }
136        break;
137
138      case '~':
139        ptr = strchr(++from, '/');
140        len = ptr ? ptr - from : strlen(from);
141        if (len == 0)
142          pwd = getpwuid(ID0realuid());
143        else {
144          strncpy(to, from, len);
145          to[len] = '\0';
146          pwd = getpwnam(to);
147        }
148        if (pwd == NULL)
149          *to++ = '~';
150        else {
151          strncpy(to, pwd->pw_dir, endto - to);
152          *endto = '\0';
153          to += strlen(to);
154          from += len;
155        }
156        endpwent();
157        break;
158
159      default:
160        *to++ = *from++;
161        break;
162    }
163  }
164
165  while (to > startto) {
166    to--;
167    if (!issep(*to)) {
168      to++;
169      break;
170    }
171  }
172  *to = '\0';
173
174  return from;
175}
176
177#define CTRL_UNKNOWN (0)
178#define CTRL_INCLUDE (1)
179
180static int
181DecodeCtrlCommand(char *line, char *arg)
182{
183  const char *end;
184
185  if (!strncasecmp(line, "include", 7) && issep(line[7])) {
186    end = InterpretArg(line+8, arg);
187    if (*end && *end != '#')
188      log_Printf(LogWARN, "usage: !include filename\n");
189    else
190      return CTRL_INCLUDE;
191  }
192  return CTRL_UNKNOWN;
193}
194
195/*
196 * Initialised in system_IsValid(), set in ReadSystem(),
197 * used by system_IsValid()
198 */
199static int modeok;
200static int userok;
201static int modereq;
202
203int
204AllowUsers(struct cmdargs const *arg)
205{
206  /* arg->bundle may be NULL (see system_IsValid()) ! */
207  int f;
208  struct passwd *pwd;
209
210  if (userok == -1)
211    userok = 0;
212
213  pwd = getpwuid(ID0realuid());
214  if (pwd != NULL)
215    for (f = arg->argn; f < arg->argc; f++)
216      if (!strcmp("*", arg->argv[f]) || !strcmp(pwd->pw_name, arg->argv[f])) {
217        userok = 1;
218        break;
219      }
220  endpwent();
221
222  return 0;
223}
224
225int
226AllowModes(struct cmdargs const *arg)
227{
228  /* arg->bundle may be NULL (see system_IsValid()) ! */
229  int f, mode, allowed;
230
231  allowed = 0;
232  for (f = arg->argn; f < arg->argc; f++) {
233    mode = Nam2mode(arg->argv[f]);
234    if (mode == PHYS_NONE || mode == PHYS_ALL)
235      log_Printf(LogWARN, "allow modes: %s: Invalid mode\n", arg->argv[f]);
236    else
237      allowed |= mode;
238  }
239
240  modeok = modereq & allowed ? 1 : 0;
241  return 0;
242}
243
244static char *
245strip(char *line)
246{
247  int len;
248
249  len = strlen(line);
250  while (len && (line[len-1] == '\n' || line[len-1] == '\r' ||
251                 issep(line[len-1])))
252    line[--len] = '\0';
253
254  while (issep(*line))
255    line++;
256
257  if (*line == '#')
258    *line = '\0';
259
260  return line;
261}
262
263static int
264xgets(char *buf, int buflen, FILE *fp)
265{
266  int len, n;
267
268  n = 0;
269  while (fgets(buf, buflen-1, fp)) {
270    n++;
271    buf[buflen-1] = '\0';
272    len = strlen(buf);
273    while (len && (buf[len-1] == '\n' || buf[len-1] == '\r'))
274      buf[--len] = '\0';
275    if (len && buf[len-1] == '\\') {
276      buf += len - 1;
277      buflen -= len - 1;
278      if (!buflen)        /* No buffer space */
279        break;
280    } else
281      break;
282  }
283  return n;
284}
285
286/* Values for ``how'' in ReadSystem */
287#define SYSTEM_EXISTS	1
288#define SYSTEM_VALIDATE	2
289#define SYSTEM_EXEC	3
290
291static char *
292GetLabel(char *line, const char *filename, int linenum)
293{
294  char *argv[MAXARGS];
295  int argc, len;
296
297  argc = MakeArgs(line, argv, MAXARGS, PARSE_REDUCE);
298
299  if (argc == 2 && !strcmp(argv[1], ":"))
300    return argv[0];
301
302  if (argc != 1 || (len = strlen(argv[0])) < 2 || argv[0][len-1] != ':') {
303      log_Printf(LogWARN, "Bad label in %s (line %d) - missing colon\n",
304                 filename, linenum);
305      return NULL;
306  }
307  argv[0][len-1] = '\0';	/* Lose the ':' */
308
309  return argv[0];
310}
311
312/* Returns -2 for ``file not found'' and -1 for ``label not found'' */
313
314static int
315ReadSystem(struct bundle *bundle, const char *name, const char *file,
316           struct prompt *prompt, struct datalink *cx, int how)
317{
318  FILE *fp;
319  char *cp;
320  int n, len;
321  char line[LINE_LEN];
322  char filename[PATH_MAX];
323  int linenum;
324  int argc;
325  char *argv[MAXARGS];
326  int allowcmd;
327  int indent;
328  char arg[LINE_LEN];
329  struct prompt *op;
330
331  if (*file == '/')
332    snprintf(filename, sizeof filename, "%s", file);
333  else
334    snprintf(filename, sizeof filename, "%s/%s", PPP_CONFDIR, file);
335  fp = ID0fopen(filename, "r");
336  if (fp == NULL) {
337    log_Printf(LogDEBUG, "ReadSystem: Can't open %s.\n", filename);
338    return -2;
339  }
340  log_Printf(LogDEBUG, "ReadSystem: Checking %s (%s).\n", name, filename);
341
342  linenum = 0;
343  while ((n = xgets(line, sizeof line, fp))) {
344    linenum += n;
345    if (issep(*line))
346      continue;
347
348    cp = strip(line);
349
350    switch (*cp) {
351    case '\0':			/* empty/comment */
352      break;
353
354    case '!':
355      switch (DecodeCtrlCommand(cp+1, arg)) {
356      case CTRL_INCLUDE:
357        log_Printf(LogCOMMAND, "%s: Including \"%s\"\n", filename, arg);
358        n = ReadSystem(bundle, name, arg, prompt, cx, how);
359        log_Printf(LogCOMMAND, "%s: Done include of \"%s\"\n", filename, arg);
360        if (!n) {
361          fclose(fp);
362          return 0;	/* got it */
363        }
364        break;
365      default:
366        log_Printf(LogWARN, "%s: %s: Invalid command\n", filename, cp);
367        break;
368      }
369      break;
370
371    default:
372      if ((cp = GetLabel(cp, filename, linenum)) == NULL)
373        continue;
374
375      if (strcmp(cp, name) == 0) {
376        /* We're in business */
377        if (how == SYSTEM_EXISTS) {
378          fclose(fp);
379	  return 0;
380	}
381	while ((n = xgets(line, sizeof line, fp))) {
382          linenum += n;
383          indent = issep(*line);
384          cp = strip(line);
385
386          if (*cp == '\0')			/* empty / comment */
387            continue;
388
389          if (!indent) {			/* start of next section */
390            if (*cp != '!' && how == SYSTEM_EXEC)
391              cp = GetLabel(cp, filename, linenum);
392            break;
393          }
394
395          len = strlen(cp);
396          if ((argc = command_Expand_Interpret(cp, len, argv, cp - line)) < 0)
397            log_Printf(LogWARN, "%s: %d: Syntax error\n", filename, linenum);
398          else {
399            allowcmd = argc > 0 && !strcasecmp(argv[0], "allow");
400            if ((how != SYSTEM_EXEC && allowcmd) ||
401                (how == SYSTEM_EXEC && !allowcmd)) {
402              /*
403               * Disable any context so that warnings are given to everyone,
404               * including syslog.
405               */
406              op = log_PromptContext;
407              log_PromptContext = NULL;
408	      command_Run(bundle, argc, (char const *const *)argv, prompt,
409                          name, cx);
410              log_PromptContext = op;
411            }
412          }
413        }
414
415	fclose(fp);  /* everything read - get out */
416	return 0;
417      }
418      break;
419    }
420  }
421  fclose(fp);
422  return -1;
423}
424
425const char *
426system_IsValid(const char *name, struct prompt *prompt, int mode)
427{
428  /*
429   * Note:  The ReadSystem() calls only result in calls to the Allow*
430   * functions.  arg->bundle will be set to NULL for these commands !
431   */
432  int def, how, rs;
433  int defuserok;
434
435  def = !strcmp(name, "default");
436  how = ID0realuid() == 0 ? SYSTEM_EXISTS : SYSTEM_VALIDATE;
437  userok = -1;
438  modeok = 1;
439  modereq = mode;
440
441  rs = ReadSystem(NULL, "default", CONFFILE, prompt, NULL, how);
442
443  defuserok = userok;
444  userok = -1;
445
446  if (!def) {
447    if (rs == -1)
448      rs = 0;		/* we don't care that ``default'' doesn't exist */
449
450    if (rs == 0)
451      rs = ReadSystem(NULL, name, CONFFILE, prompt, NULL, how);
452
453    if (rs == -1)
454      return "Configuration label not found";
455
456    if (rs == -2)
457      return PPP_CONFDIR "/" CONFFILE " : File not found";
458  }
459
460  if (userok == -1)
461    userok = defuserok;
462
463  if (how == SYSTEM_EXISTS)
464    userok = modeok = 1;
465
466  if (!userok)
467    return "User access denied";
468
469  if (!modeok)
470    return "Mode denied for this label";
471
472  return NULL;
473}
474
475int
476system_Select(struct bundle *bundle, const char *name, const char *file,
477             struct prompt *prompt, struct datalink *cx)
478{
479  userok = modeok = 1;
480  modereq = PHYS_ALL;
481  return ReadSystem(bundle, name, file, prompt, cx, SYSTEM_EXEC);
482}
483