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