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