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