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