178189Sbrian/*-
278189Sbrian * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
378189Sbrian *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
478189Sbrian *                           Internet Initiative Japan, Inc (IIJ)
578189Sbrian * All rights reserved.
66059Samurai *
778189Sbrian * Redistribution and use in source and binary forms, with or without
878189Sbrian * modification, are permitted provided that the following conditions
978189Sbrian * are met:
1078189Sbrian * 1. Redistributions of source code must retain the above copyright
1178189Sbrian *    notice, this list of conditions and the following disclaimer.
1278189Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1378189Sbrian *    notice, this list of conditions and the following disclaimer in the
1478189Sbrian *    documentation and/or other materials provided with the distribution.
156059Samurai *
1678189Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1778189Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1878189Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1978189Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2078189Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2178189Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2278189Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2378189Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2478189Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2578189Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2678189Sbrian * SUCH DAMAGE.
276059Samurai *
2850479Speter * $FreeBSD$
296059Samurai */
3078189Sbrian
3130715Sbrian#include <sys/param.h>
3230715Sbrian
3331077Sbrian#include <ctype.h>
3431077Sbrian#include <pwd.h>
3530715Sbrian#include <stdio.h>
3630715Sbrian#include <stdlib.h>
3730715Sbrian#include <string.h>
3846686Sbrian#include <termios.h>
3930715Sbrian
4037009Sbrian#include "defs.h"
4131343Sbrian#include "command.h"
4230715Sbrian#include "log.h"
4331061Sbrian#include "id.h"
4430715Sbrian#include "systems.h"
456059Samurai
4631077Sbrian#define issep(ch) ((ch) == ' ' || (ch) == '\t')
4731077Sbrian
486059SamuraiFILE *
4931343SbrianOpenSecret(const char *file)
506059Samurai{
516059Samurai  FILE *fp;
526059Samurai  char line[100];
536059Samurai
5474687Sbrian  snprintf(line, sizeof line, "%s/%s", PPP_CONFDIR, file);
5531061Sbrian  fp = ID0fopen(line, "r");
5631061Sbrian  if (fp == NULL)
5736285Sbrian    log_Printf(LogWARN, "OpenSecret: Can't open %s.\n", line);
5828679Sbrian  return (fp);
596059Samurai}
606059Samurai
616059Samuraivoid
6246828SbrianCloseSecret(FILE *fp)
636059Samurai{
646059Samurai  fclose(fp);
656059Samurai}
666059Samurai
6731077Sbrian/* Move string from ``from'' to ``to'', interpreting ``~'' and $.... */
6858045Sbrianconst char *
6954915SbrianInterpretArg(const char *from, char *to)
7031077Sbrian{
7158045Sbrian  char *ptr, *startto, *endto;
7258045Sbrian  struct passwd *pwd;
73134789Sbrian  int instring;
74134789Sbrian  size_t len;
7531077Sbrian  const char *env;
7631077Sbrian
7758045Sbrian  instring = 0;
7831077Sbrian  startto = to;
7931077Sbrian  endto = to + LINE_LEN - 1;
8031077Sbrian
8131077Sbrian  while(issep(*from))
8231077Sbrian    from++;
8354915Sbrian
8458045Sbrian  while (*from != '\0') {
8558045Sbrian    switch (*from) {
8658045Sbrian      case '"':
8758045Sbrian        instring = !instring;
8858045Sbrian        *to++ = *from++;
8958045Sbrian        break;
9058045Sbrian      case '\\':
9158045Sbrian        switch (*++from) {
9258045Sbrian          case '$':
9358045Sbrian          case '~':
9458045Sbrian            break;		/* Swallow the escapes */
9554919Sbrian
9658045Sbrian          default:
9758045Sbrian            *to++ = '\\';	/* Pass the escapes on, maybe skipping \# */
9858045Sbrian            break;
9958045Sbrian        }
10058045Sbrian        *to++ = *from++;
10158045Sbrian        break;
10258045Sbrian      case '$':
10358045Sbrian        if (from[1] == '$') {
10458045Sbrian          *to = '\0';	/* For an empty var name below */
10558045Sbrian          from += 2;
10658045Sbrian        } else if (from[1] == '{') {
10758045Sbrian          ptr = strchr(from+2, '}');
10858045Sbrian          if (ptr) {
10958045Sbrian            len = ptr - from - 2;
110134789Sbrian            if (endto - to < (int)len )
11158045Sbrian              len = endto - to;
11258045Sbrian            if (len) {
11358045Sbrian              strncpy(to, from+2, len);
11458045Sbrian              to[len] = '\0';
11558045Sbrian              from = ptr+1;
11658045Sbrian            } else {
11758045Sbrian              *to++ = *from++;
11858045Sbrian              continue;
11958045Sbrian            }
12031077Sbrian          } else {
12131077Sbrian            *to++ = *from++;
12231077Sbrian            continue;
12331077Sbrian          }
12431077Sbrian        } else {
12558045Sbrian          ptr = to;
12658045Sbrian          for (from++; (isalnum(*from) || *from == '_') && ptr < endto; from++)
12758045Sbrian            *ptr++ = *from;
12858045Sbrian          *ptr = '\0';
12931077Sbrian        }
13058045Sbrian        if (*to == '\0')
13158045Sbrian          *to++ = '$';
13258045Sbrian        else if ((env = getenv(to)) != NULL) {
13358045Sbrian          strncpy(to, env, endto - to);
13458045Sbrian          *endto = '\0';
13558045Sbrian          to += strlen(to);
13658045Sbrian        }
13758045Sbrian        break;
13858045Sbrian
13958045Sbrian      case '~':
14058045Sbrian        ptr = strchr(++from, '/');
141134789Sbrian        len = ptr ? (size_t)(ptr - from) : strlen(from);
14258045Sbrian        if (len == 0)
14358045Sbrian          pwd = getpwuid(ID0realuid());
14458045Sbrian        else {
14558045Sbrian          strncpy(to, from, len);
14658045Sbrian          to[len] = '\0';
14758045Sbrian          pwd = getpwnam(to);
14858045Sbrian        }
14958045Sbrian        if (pwd == NULL)
15058045Sbrian          *to++ = '~';
15158045Sbrian        else {
15258045Sbrian          strncpy(to, pwd->pw_dir, endto - to);
15358045Sbrian          *endto = '\0';
15458045Sbrian          to += strlen(to);
15558045Sbrian          from += len;
15658045Sbrian        }
15758045Sbrian        endpwent();
15858045Sbrian        break;
15958045Sbrian
16058045Sbrian      default:
16158045Sbrian        *to++ = *from++;
16258045Sbrian        break;
16354915Sbrian    }
16431077Sbrian  }
16554915Sbrian
16631077Sbrian  while (to > startto) {
16731077Sbrian    to--;
16831077Sbrian    if (!issep(*to)) {
16931077Sbrian      to++;
17031077Sbrian      break;
17131077Sbrian    }
17231077Sbrian  }
17331077Sbrian  *to = '\0';
17454915Sbrian
17554915Sbrian  return from;
17631077Sbrian}
17731077Sbrian
17831077Sbrian#define CTRL_UNKNOWN (0)
17931077Sbrian#define CTRL_INCLUDE (1)
18031077Sbrian
18131077Sbrianstatic int
18231077SbrianDecodeCtrlCommand(char *line, char *arg)
18331077Sbrian{
18454915Sbrian  const char *end;
18554915Sbrian
18631077Sbrian  if (!strncasecmp(line, "include", 7) && issep(line[7])) {
18754915Sbrian    end = InterpretArg(line+8, arg);
18854915Sbrian    if (*end && *end != '#')
18995258Sdes      log_Printf(LogWARN, "usage: !include filename\n");
19054915Sbrian    else
19154915Sbrian      return CTRL_INCLUDE;
19231077Sbrian  }
19331077Sbrian  return CTRL_UNKNOWN;
19431077Sbrian}
19531077Sbrian
19640797Sbrian/*
19740797Sbrian * Initialised in system_IsValid(), set in ReadSystem(),
19840797Sbrian * used by system_IsValid()
19940797Sbrian */
20036285Sbrianstatic int modeok;
20131121Sbrianstatic int userok;
20236285Sbrianstatic int modereq;
20331121Sbrian
2046059Samuraiint
20531343SbrianAllowUsers(struct cmdargs const *arg)
2066059Samurai{
20736285Sbrian  /* arg->bundle may be NULL (see system_IsValid()) ! */
20831121Sbrian  int f;
20954918Sbrian  struct passwd *pwd;
21031121Sbrian
21163052Sbrian  if (userok == -1)
21263052Sbrian    userok = 0;
21363052Sbrian
21455252Sbrian  pwd = getpwuid(ID0realuid());
21554918Sbrian  if (pwd != NULL)
21636285Sbrian    for (f = arg->argn; f < arg->argc; f++)
21754918Sbrian      if (!strcmp("*", arg->argv[f]) || !strcmp(pwd->pw_name, arg->argv[f])) {
21831121Sbrian        userok = 1;
21931121Sbrian        break;
22031121Sbrian      }
22154918Sbrian  endpwent();
22231121Sbrian
22331121Sbrian  return 0;
22431121Sbrian}
22531121Sbrian
22631121Sbrianint
22731343SbrianAllowModes(struct cmdargs const *arg)
22831121Sbrian{
22936285Sbrian  /* arg->bundle may be NULL (see system_IsValid()) ! */
23036285Sbrian  int f, mode, allowed;
23131121Sbrian
23231121Sbrian  allowed = 0;
23336285Sbrian  for (f = arg->argn; f < arg->argc; f++) {
23436285Sbrian    mode = Nam2mode(arg->argv[f]);
23536285Sbrian    if (mode == PHYS_NONE || mode == PHYS_ALL)
23636285Sbrian      log_Printf(LogWARN, "allow modes: %s: Invalid mode\n", arg->argv[f]);
23736285Sbrian    else
23836285Sbrian      allowed |= mode;
23931121Sbrian  }
24031121Sbrian
24136285Sbrian  modeok = modereq & allowed ? 1 : 0;
24231121Sbrian  return 0;
24331121Sbrian}
24431121Sbrian
24536285Sbrianstatic char *
24636285Sbrianstrip(char *line)
24736285Sbrian{
24836285Sbrian  int len;
24936285Sbrian
25036285Sbrian  len = strlen(line);
25136285Sbrian  while (len && (line[len-1] == '\n' || line[len-1] == '\r' ||
25236285Sbrian                 issep(line[len-1])))
25336285Sbrian    line[--len] = '\0';
25436285Sbrian
25536285Sbrian  while (issep(*line))
25636285Sbrian    line++;
25736285Sbrian
25836285Sbrian  if (*line == '#')
25936285Sbrian    *line = '\0';
26036285Sbrian
26136285Sbrian  return line;
26236285Sbrian}
26336285Sbrian
26431121Sbrianstatic int
26536285Sbrianxgets(char *buf, int buflen, FILE *fp)
26631121Sbrian{
26736285Sbrian  int len, n;
26836285Sbrian
26936285Sbrian  n = 0;
27036285Sbrian  while (fgets(buf, buflen-1, fp)) {
27136285Sbrian    n++;
27236285Sbrian    buf[buflen-1] = '\0';
27336285Sbrian    len = strlen(buf);
27436285Sbrian    while (len && (buf[len-1] == '\n' || buf[len-1] == '\r'))
27536285Sbrian      buf[--len] = '\0';
27636285Sbrian    if (len && buf[len-1] == '\\') {
27736285Sbrian      buf += len - 1;
27836285Sbrian      buflen -= len - 1;
27936285Sbrian      if (!buflen)        /* No buffer space */
28036285Sbrian        break;
28136285Sbrian    } else
28236285Sbrian      break;
28336285Sbrian  }
28436285Sbrian  return n;
28536285Sbrian}
28636285Sbrian
28743526Sbrian/* Values for ``how'' in ReadSystem */
28843526Sbrian#define SYSTEM_EXISTS	1
28943526Sbrian#define SYSTEM_VALIDATE	2
29043526Sbrian#define SYSTEM_EXEC	3
29143526Sbrian
29255064Sbrianstatic char *
29355064SbrianGetLabel(char *line, const char *filename, int linenum)
29455064Sbrian{
29555145Sbrian  char *argv[MAXARGS];
29655145Sbrian  int argc, len;
29755064Sbrian
29855145Sbrian  argc = MakeArgs(line, argv, MAXARGS, PARSE_REDUCE);
29955145Sbrian
30055145Sbrian  if (argc == 2 && !strcmp(argv[1], ":"))
30155145Sbrian    return argv[0];
30255145Sbrian
30355145Sbrian  if (argc != 1 || (len = strlen(argv[0])) < 2 || argv[0][len-1] != ':') {
30455145Sbrian      log_Printf(LogWARN, "Bad label in %s (line %d) - missing colon\n",
30555064Sbrian                 filename, linenum);
30655064Sbrian      return NULL;
30755064Sbrian  }
30855145Sbrian  argv[0][len-1] = '\0';	/* Lose the ':' */
30955064Sbrian
31055145Sbrian  return argv[0];
31155064Sbrian}
31255064Sbrian
31344612Sbrian/* Returns -2 for ``file not found'' and -1 for ``label not found'' */
31444612Sbrian
31536285Sbrianstatic int
31636285SbrianReadSystem(struct bundle *bundle, const char *name, const char *file,
31743526Sbrian           struct prompt *prompt, struct datalink *cx, int how)
31836285Sbrian{
3196059Samurai  FILE *fp;
32055064Sbrian  char *cp;
32130913Sbrian  int n, len;
32231070Sbrian  char line[LINE_LEN];
32374001Sbrian  char filename[PATH_MAX];
32425630Sbrian  int linenum;
32531121Sbrian  int argc;
32637009Sbrian  char *argv[MAXARGS];
32731121Sbrian  int allowcmd;
32836285Sbrian  int indent;
32936285Sbrian  char arg[LINE_LEN];
33051005Sbrian  struct prompt *op;
3316059Samurai
33231077Sbrian  if (*file == '/')
33331077Sbrian    snprintf(filename, sizeof filename, "%s", file);
33431077Sbrian  else
33574687Sbrian    snprintf(filename, sizeof filename, "%s/%s", PPP_CONFDIR, file);
33631061Sbrian  fp = ID0fopen(filename, "r");
3376059Samurai  if (fp == NULL) {
33836285Sbrian    log_Printf(LogDEBUG, "ReadSystem: Can't open %s.\n", filename);
33944612Sbrian    return -2;
3406059Samurai  }
34136285Sbrian  log_Printf(LogDEBUG, "ReadSystem: Checking %s (%s).\n", name, filename);
34225630Sbrian
34325630Sbrian  linenum = 0;
34436285Sbrian  while ((n = xgets(line, sizeof line, fp))) {
34536285Sbrian    linenum += n;
34636285Sbrian    if (issep(*line))
34736285Sbrian      continue;
34836285Sbrian
34936285Sbrian    cp = strip(line);
35036285Sbrian
3516059Samurai    switch (*cp) {
35236285Sbrian    case '\0':			/* empty/comment */
3536059Samurai      break;
35436285Sbrian
35536285Sbrian    case '!':
35636285Sbrian      switch (DecodeCtrlCommand(cp+1, arg)) {
35736285Sbrian      case CTRL_INCLUDE:
35836285Sbrian        log_Printf(LogCOMMAND, "%s: Including \"%s\"\n", filename, arg);
35943526Sbrian        n = ReadSystem(bundle, name, arg, prompt, cx, how);
36036285Sbrian        log_Printf(LogCOMMAND, "%s: Done include of \"%s\"\n", filename, arg);
36158983Sbrian        if (!n) {
36258983Sbrian          fclose(fp);
36336285Sbrian          return 0;	/* got it */
36458983Sbrian        }
36536285Sbrian        break;
36636285Sbrian      default:
36736285Sbrian        log_Printf(LogWARN, "%s: %s: Invalid command\n", filename, cp);
36836285Sbrian        break;
36936285Sbrian      }
3706059Samurai      break;
37136285Sbrian
3726059Samurai    default:
37355064Sbrian      if ((cp = GetLabel(cp, filename, linenum)) == NULL)
37455064Sbrian        continue;
37536285Sbrian
37636285Sbrian      if (strcmp(cp, name) == 0) {
37736285Sbrian        /* We're in business */
37858983Sbrian        if (how == SYSTEM_EXISTS) {
37958983Sbrian          fclose(fp);
38043526Sbrian	  return 0;
38158983Sbrian	}
38236285Sbrian	while ((n = xgets(line, sizeof line, fp))) {
38336285Sbrian          linenum += n;
38436285Sbrian          indent = issep(*line);
38536285Sbrian          cp = strip(line);
38636285Sbrian
38755064Sbrian          if (*cp == '\0')			/* empty / comment */
38836285Sbrian            continue;
38936285Sbrian
39055064Sbrian          if (!indent) {			/* start of next section */
39155064Sbrian            if (*cp != '!' && how == SYSTEM_EXEC)
39255145Sbrian              cp = GetLabel(cp, filename, linenum);
39336285Sbrian            break;
39440481Sbrian          }
39536285Sbrian
39636285Sbrian          len = strlen(cp);
39758045Sbrian          if ((argc = command_Expand_Interpret(cp, len, argv, cp - line)) < 0)
39854914Sbrian            log_Printf(LogWARN, "%s: %d: Syntax error\n", filename, linenum);
39954914Sbrian          else {
40054914Sbrian            allowcmd = argc > 0 && !strcasecmp(argv[0], "allow");
40154916Sbrian            if ((how != SYSTEM_EXEC && allowcmd) ||
40254916Sbrian                (how == SYSTEM_EXEC && !allowcmd)) {
40354914Sbrian              /*
40454914Sbrian               * Disable any context so that warnings are given to everyone,
40554914Sbrian               * including syslog.
40654914Sbrian               */
40754914Sbrian              op = log_PromptContext;
40854914Sbrian              log_PromptContext = NULL;
40954914Sbrian	      command_Run(bundle, argc, (char const *const *)argv, prompt,
41054914Sbrian                          name, cx);
41154914Sbrian              log_PromptContext = op;
41254914Sbrian            }
41351005Sbrian          }
41431077Sbrian        }
41536285Sbrian
41636285Sbrian	fclose(fp);  /* everything read - get out */
41736285Sbrian	return 0;
4186059Samurai      }
4196059Samurai      break;
4206059Samurai    }
4216059Samurai  }
4226059Samurai  fclose(fp);
42326516Sbrian  return -1;
4246059Samurai}
4256059Samurai
42640797Sbrianconst char *
42736285Sbriansystem_IsValid(const char *name, struct prompt *prompt, int mode)
42831121Sbrian{
42936285Sbrian  /*
43036285Sbrian   * Note:  The ReadSystem() calls only result in calls to the Allow*
43136285Sbrian   * functions.  arg->bundle will be set to NULL for these commands !
43236285Sbrian   */
43344612Sbrian  int def, how, rs;
43463052Sbrian  int defuserok;
43540797Sbrian
43640797Sbrian  def = !strcmp(name, "default");
43743526Sbrian  how = ID0realuid() == 0 ? SYSTEM_EXISTS : SYSTEM_VALIDATE;
43863052Sbrian  userok = -1;
43931121Sbrian  modeok = 1;
44036285Sbrian  modereq = mode;
44140797Sbrian
44244612Sbrian  rs = ReadSystem(NULL, "default", CONFFILE, prompt, NULL, how);
44340797Sbrian
44463052Sbrian  defuserok = userok;
44563052Sbrian  userok = -1;
44663052Sbrian
44744612Sbrian  if (!def) {
44844612Sbrian    if (rs == -1)
44944612Sbrian      rs = 0;		/* we don't care that ``default'' doesn't exist */
45040797Sbrian
45144612Sbrian    if (rs == 0)
45244612Sbrian      rs = ReadSystem(NULL, name, CONFFILE, prompt, NULL, how);
45344612Sbrian
45444612Sbrian    if (rs == -1)
45544612Sbrian      return "Configuration label not found";
45644612Sbrian
45744612Sbrian    if (rs == -2)
45874687Sbrian      return PPP_CONFDIR "/" CONFFILE " : File not found";
45944612Sbrian  }
46044612Sbrian
46163052Sbrian  if (userok == -1)
46263052Sbrian    userok = defuserok;
46363052Sbrian
46443526Sbrian  if (how == SYSTEM_EXISTS)
46543526Sbrian    userok = modeok = 1;
46643526Sbrian
46740797Sbrian  if (!userok)
46843526Sbrian    return "User access denied";
46940797Sbrian
47040797Sbrian  if (!modeok)
47143526Sbrian    return "Mode denied for this label";
47240797Sbrian
47340797Sbrian  return NULL;
47431121Sbrian}
47531121Sbrian
47631121Sbrianint
47736285Sbriansystem_Select(struct bundle *bundle, const char *name, const char *file,
47837008Sbrian             struct prompt *prompt, struct datalink *cx)
47931121Sbrian{
48031121Sbrian  userok = modeok = 1;
48136285Sbrian  modereq = PHYS_ALL;
48243526Sbrian  return ReadSystem(bundle, name, file, prompt, cx, SYSTEM_EXEC);
48331121Sbrian}
484