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