xref: /freebsd/usr.sbin/ppp/systems.c (revision 2008043f386721d58158e37e0d7e50df8095942d)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
5  *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
6  *                           Internet Initiative Japan, Inc (IIJ)
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
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 instring;
74   size_t len;
75   const char *env;
76 
77   instring = 0;
78   startto = to;
79   endto = to + LINE_LEN - 1;
80 
81   while(issep(*from))
82     from++;
83 
84   while (*from != '\0') {
85     switch (*from) {
86       case '"':
87         instring = !instring;
88         *to++ = *from++;
89         break;
90       case '\\':
91         switch (*++from) {
92           case '$':
93           case '~':
94             break;		/* Swallow the escapes */
95 
96           default:
97             *to++ = '\\';	/* Pass the escapes on, maybe skipping \# */
98             break;
99         }
100         *to++ = *from++;
101         break;
102       case '$':
103         if (from[1] == '$') {
104           *to = '\0';	/* For an empty var name below */
105           from += 2;
106         } else if (from[1] == '{') {
107           ptr = strchr(from+2, '}');
108           if (ptr) {
109             len = ptr - from - 2;
110             if (endto - to < (int)len )
111               len = endto - to;
112             if (len) {
113               strncpy(to, from+2, len);
114               to[len] = '\0';
115               from = ptr+1;
116             } else {
117               *to++ = *from++;
118               continue;
119             }
120           } else {
121             *to++ = *from++;
122             continue;
123           }
124         } else {
125           ptr = to;
126           for (from++; (isalnum(*from) || *from == '_') && ptr < endto; from++)
127             *ptr++ = *from;
128           *ptr = '\0';
129         }
130         if (*to == '\0')
131           *to++ = '$';
132         else if ((env = getenv(to)) != NULL) {
133           strncpy(to, env, endto - to);
134           *endto = '\0';
135           to += strlen(to);
136         }
137         break;
138 
139       case '~':
140         ptr = strchr(++from, '/');
141         len = ptr ? (size_t)(ptr - from) : strlen(from);
142         if (len == 0)
143           pwd = getpwuid(ID0realuid());
144         else {
145           strncpy(to, from, len);
146           to[len] = '\0';
147           pwd = getpwnam(to);
148         }
149         if (pwd == NULL)
150           *to++ = '~';
151         else {
152           strncpy(to, pwd->pw_dir, endto - to);
153           *endto = '\0';
154           to += strlen(to);
155           from += len;
156         }
157         endpwent();
158         break;
159 
160       default:
161         *to++ = *from++;
162         break;
163     }
164   }
165 
166   while (to > startto) {
167     to--;
168     if (!issep(*to)) {
169       to++;
170       break;
171     }
172   }
173   *to = '\0';
174 
175   return from;
176 }
177 
178 #define CTRL_UNKNOWN (0)
179 #define CTRL_INCLUDE (1)
180 
181 static int
182 DecodeCtrlCommand(char *line, char *arg)
183 {
184   const char *end;
185 
186   if (!strncasecmp(line, "include", 7) && issep(line[7])) {
187     end = InterpretArg(line+8, arg);
188     if (*end && *end != '#')
189       log_Printf(LogWARN, "usage: !include filename\n");
190     else
191       return CTRL_INCLUDE;
192   }
193   return CTRL_UNKNOWN;
194 }
195 
196 /*
197  * Initialised in system_IsValid(), set in ReadSystem(),
198  * used by system_IsValid()
199  */
200 static int modeok;
201 static int userok;
202 static int modereq;
203 
204 int
205 AllowUsers(struct cmdargs const *arg)
206 {
207   /* arg->bundle may be NULL (see system_IsValid()) ! */
208   int f;
209   struct passwd *pwd;
210 
211   if (userok == -1)
212     userok = 0;
213 
214   pwd = getpwuid(ID0realuid());
215   if (pwd != NULL)
216     for (f = arg->argn; f < arg->argc; f++)
217       if (!strcmp("*", arg->argv[f]) || !strcmp(pwd->pw_name, arg->argv[f])) {
218         userok = 1;
219         break;
220       }
221   endpwent();
222 
223   return 0;
224 }
225 
226 int
227 AllowModes(struct cmdargs const *arg)
228 {
229   /* arg->bundle may be NULL (see system_IsValid()) ! */
230   int f, mode, allowed;
231 
232   allowed = 0;
233   for (f = arg->argn; f < arg->argc; f++) {
234     mode = Nam2mode(arg->argv[f]);
235     if (mode == PHYS_NONE || mode == PHYS_ALL)
236       log_Printf(LogWARN, "allow modes: %s: Invalid mode\n", arg->argv[f]);
237     else
238       allowed |= mode;
239   }
240 
241   modeok = modereq & allowed ? 1 : 0;
242   return 0;
243 }
244 
245 static char *
246 strip(char *line)
247 {
248   int len;
249 
250   len = strlen(line);
251   while (len && (line[len-1] == '\n' || line[len-1] == '\r' ||
252                  issep(line[len-1])))
253     line[--len] = '\0';
254 
255   while (issep(*line))
256     line++;
257 
258   if (*line == '#')
259     *line = '\0';
260 
261   return line;
262 }
263 
264 static int
265 xgets(char *buf, int buflen, FILE *fp)
266 {
267   int len, n;
268 
269   n = 0;
270   while (fgets(buf, buflen-1, fp)) {
271     n++;
272     buf[buflen-1] = '\0';
273     len = strlen(buf);
274     while (len && (buf[len-1] == '\n' || buf[len-1] == '\r'))
275       buf[--len] = '\0';
276     if (len && buf[len-1] == '\\') {
277       buf += len - 1;
278       buflen -= len - 1;
279       if (!buflen)        /* No buffer space */
280         break;
281     } else
282       break;
283   }
284   return n;
285 }
286 
287 /* Values for ``how'' in ReadSystem */
288 #define SYSTEM_EXISTS	1
289 #define SYSTEM_VALIDATE	2
290 #define SYSTEM_EXEC	3
291 
292 static char *
293 GetLabel(char *line, const char *filename, int linenum)
294 {
295   char *argv[MAXARGS];
296   int argc, len;
297 
298   argc = MakeArgs(line, argv, MAXARGS, PARSE_REDUCE);
299 
300   if (argc == 2 && !strcmp(argv[1], ":"))
301     return argv[0];
302 
303   if (argc != 1 || (len = strlen(argv[0])) < 2 || argv[0][len-1] != ':') {
304       log_Printf(LogWARN, "Bad label in %s (line %d) - missing colon\n",
305                  filename, linenum);
306       return NULL;
307   }
308   argv[0][len-1] = '\0';	/* Lose the ':' */
309 
310   return argv[0];
311 }
312 
313 /* Returns -2 for ``file not found'' and -1 for ``label not found'' */
314 
315 static int
316 ReadSystem(struct bundle *bundle, const char *name, const char *file,
317            struct prompt *prompt, struct datalink *cx, int how)
318 {
319   FILE *fp;
320   char *cp;
321   int n, len;
322   char line[LINE_LEN];
323   char filename[PATH_MAX];
324   int linenum;
325   int argc;
326   char *argv[MAXARGS];
327   int allowcmd;
328   int indent;
329   char arg[LINE_LEN];
330   struct prompt *op;
331 
332   if (*file == '/')
333     snprintf(filename, sizeof filename, "%s", file);
334   else
335     snprintf(filename, sizeof filename, "%s/%s", PPP_CONFDIR, file);
336   fp = ID0fopen(filename, "r");
337   if (fp == NULL) {
338     log_Printf(LogDEBUG, "ReadSystem: Can't open %s.\n", filename);
339     return -2;
340   }
341   log_Printf(LogDEBUG, "ReadSystem: Checking %s (%s).\n", name, filename);
342 
343   linenum = 0;
344   while ((n = xgets(line, sizeof line, fp))) {
345     linenum += n;
346     if (issep(*line))
347       continue;
348 
349     cp = strip(line);
350 
351     switch (*cp) {
352     case '\0':			/* empty/comment */
353       break;
354 
355     case '!':
356       switch (DecodeCtrlCommand(cp+1, arg)) {
357       case CTRL_INCLUDE:
358         log_Printf(LogCOMMAND, "%s: Including \"%s\"\n", filename, arg);
359         n = ReadSystem(bundle, name, arg, prompt, cx, how);
360         log_Printf(LogCOMMAND, "%s: Done include of \"%s\"\n", filename, arg);
361         if (!n) {
362           fclose(fp);
363           return 0;	/* got it */
364         }
365         break;
366       default:
367         log_Printf(LogWARN, "%s: %s: Invalid command\n", filename, cp);
368         break;
369       }
370       break;
371 
372     default:
373       if ((cp = GetLabel(cp, filename, linenum)) == NULL)
374         continue;
375 
376       if (strcmp(cp, name) == 0) {
377         /* We're in business */
378         if (how == SYSTEM_EXISTS) {
379           fclose(fp);
380 	  return 0;
381 	}
382 	while ((n = xgets(line, sizeof line, fp))) {
383           linenum += n;
384           indent = issep(*line);
385           cp = strip(line);
386 
387           if (*cp == '\0')			/* empty / comment */
388             continue;
389 
390           if (!indent) {			/* start of next section */
391             if (*cp != '!' && how == SYSTEM_EXEC)
392               cp = GetLabel(cp, filename, linenum);
393             break;
394           }
395 
396           len = strlen(cp);
397           if ((argc = command_Expand_Interpret(cp, len, argv, cp - line)) < 0)
398             log_Printf(LogWARN, "%s: %d: Syntax error\n", filename, linenum);
399           else {
400             allowcmd = argc > 0 && !strcasecmp(argv[0], "allow");
401             if ((how != SYSTEM_EXEC && allowcmd) ||
402                 (how == SYSTEM_EXEC && !allowcmd)) {
403               /*
404                * Disable any context so that warnings are given to everyone,
405                * including syslog.
406                */
407               op = log_PromptContext;
408               log_PromptContext = NULL;
409 	      command_Run(bundle, argc, (char const *const *)argv, prompt,
410                           name, cx);
411               log_PromptContext = op;
412             }
413           }
414         }
415 
416 	fclose(fp);  /* everything read - get out */
417 	return 0;
418       }
419       break;
420     }
421   }
422   fclose(fp);
423   return -1;
424 }
425 
426 const char *
427 system_IsValid(const char *name, struct prompt *prompt, int mode)
428 {
429   /*
430    * Note:  The ReadSystem() calls only result in calls to the Allow*
431    * functions.  arg->bundle will be set to NULL for these commands !
432    */
433   int def, how, rs;
434   int defuserok;
435 
436   def = !strcmp(name, "default");
437   how = ID0realuid() == 0 ? SYSTEM_EXISTS : SYSTEM_VALIDATE;
438   userok = -1;
439   modeok = 1;
440   modereq = mode;
441 
442   rs = ReadSystem(NULL, "default", CONFFILE, prompt, NULL, how);
443 
444   defuserok = userok;
445   userok = -1;
446 
447   if (!def) {
448     if (rs == -1)
449       rs = 0;		/* we don't care that ``default'' doesn't exist */
450 
451     if (rs == 0)
452       rs = ReadSystem(NULL, name, CONFFILE, prompt, NULL, how);
453 
454     if (rs == -1)
455       return "Configuration label not found";
456 
457     if (rs == -2)
458       return PPP_CONFDIR "/" CONFFILE " : File not found";
459   }
460 
461   if (userok == -1)
462     userok = defuserok;
463 
464   if (how == SYSTEM_EXISTS)
465     userok = modeok = 1;
466 
467   if (!userok)
468     return "User access denied";
469 
470   if (!modeok)
471     return "Mode denied for this label";
472 
473   return NULL;
474 }
475 
476 int
477 system_Select(struct bundle *bundle, const char *name, const char *file,
478              struct prompt *prompt, struct datalink *cx)
479 {
480   userok = modeok = 1;
481   modereq = PHYS_ALL;
482   return ReadSystem(bundle, name, file, prompt, cx, SYSTEM_EXEC);
483 }
484