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