1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 * $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 * 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 64 CloseSecret(FILE *fp) 65 { 66 fclose(fp); 67 } 68 69 /* Move string from ``from'' to ``to'', interpreting ``~'' and $.... */ 70 const char * 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 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 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 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 * 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 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 * 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 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 * 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 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