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