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 *
OpenSecret(const char * 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
CloseSecret(FILE * fp)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 *
InterpretArg(const char * from,char * to,size_t tosiz)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
DecodeCtrlCommand(char * line,char * arg,size_t argsiz)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
AllowUsers(struct cmdargs const * arg)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
AllowModes(struct cmdargs const * arg)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 *
strip(char * line)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
xgets(char * buf,int buflen,FILE * fp)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 *
GetLabel(char * line,const char * filename,int linenum)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
ReadSystem(struct bundle * bundle,const char * name,const char * file,struct prompt * prompt,struct datalink * cx,int how)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 *
system_IsValid(const char * name,struct prompt * prompt,int mode)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
system_Select(struct bundle * bundle,const char * name,const char * file,struct prompt * prompt,struct datalink * cx)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 }
506