1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 1996 - 2001 Brian Somers <[email protected]>
5 * based on work by Toshiharu OHNO <[email protected]>
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 *
OpenSecret(const char * 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
CloseSecret(FILE * fp)64 CloseSecret(FILE *fp)
65 {
66 fclose(fp);
67 }
68
69 /* Move string from ``from'' to ``to'', interpreting ``~'' and $.... */
70 const char *
InterpretArg(const char * from,char * to)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
DecodeCtrlCommand(char * line,char * arg)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
AllowUsers(struct cmdargs const * arg)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
AllowModes(struct cmdargs const * arg)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 *
strip(char * line)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
xgets(char * buf,int buflen,FILE * fp)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 *
GetLabel(char * line,const char * filename,int linenum)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
ReadSystem(struct bundle * bundle,const char * name,const char * file,struct prompt * prompt,struct datalink * cx,int how)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 *
system_IsValid(const char * name,struct prompt * prompt,int mode)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
system_Select(struct bundle * bundle,const char * name,const char * file,struct prompt * prompt,struct datalink * cx)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