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 * 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 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 * 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 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 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 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 * 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 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 * 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 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 * 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 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