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