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$
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 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
181static int
182DecodeCtrlCommand(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 */
200static int modeok;
201static int userok;
202static int modereq;
203
204int
205AllowUsers(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
226int
227AllowModes(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
245static char *
246strip(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
264static int
265xgets(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
292static char *
293GetLabel(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
315static int
316ReadSystem(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
426const char *
427system_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
476int
477system_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