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