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