X-Git-Url: http://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=pppd%2Foptions.c;h=c92e2ddfbf8e120253f1793693cc95a58ac93fcb;hp=53456cc8d13ead6754a11e37b39c12724777d465;hb=c65dfbde7e6096272ffacd1c69f444b36a4b707a;hpb=ab7cff041f1b8054ae5691df236fe18c1d23bfe6 diff --git a/pppd/options.c b/pppd/options.c index 53456cc..c92e2dd 100644 --- a/pppd/options.c +++ b/pppd/options.c @@ -17,14 +17,13 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#ifndef lint -static char rcsid[] = "$Id: options.c,v 1.53 1999/03/19 04:23:44 paulus Exp $"; -#endif +#define RCSID "$Id: options.c,v 1.76 2000/08/01 01:38:30 paulus Exp $" #include #include #include #include +#include #include #include #include @@ -35,6 +34,9 @@ static char rcsid[] = "$Id: options.c,v 1.53 1999/03/19 04:23:44 paulus Exp $"; #include #include #include +#ifdef PLUGIN +#include +#endif #ifdef PPP_FILTER #include #include /* XXX: To get struct pcap */ @@ -56,6 +58,8 @@ static char rcsid[] = "$Id: options.c,v 1.53 1999/03/19 04:23:44 paulus Exp $"; char *strdup __P((char *)); #endif +static const char rcsid[] = RCSID; + /* * Option variables and default values. */ @@ -65,17 +69,10 @@ int dflag = 0; /* Tell libpcap we want debugging */ int debug = 0; /* Debug flag */ int kdebugflag = 0; /* Tell kernel to print debug messages */ int default_device = 1; /* Using /dev/tty or equivalent */ -char devnam[MAXPATHLEN] = "/dev/tty"; /* Device name */ -int crtscts = 0; /* Use hardware flow control */ -bool modem = 1; /* Use modem control lines */ -int inspeed = 0; /* Input/Output speed requested */ +char devnam[MAXPATHLEN]; /* Device name */ u_int32_t netmask = 0; /* IP netmask to set on interface */ -bool lockflag = 0; /* Create lock file to lock the serial dev */ bool nodetach = 0; /* Don't detach from controlling tty */ bool updetach = 0; /* Detach once link is up */ -char *connector = NULL; /* Script to establish physical link */ -char *disconnector = NULL; /* Script to disestablish physical link */ -char *welcomer = NULL; /* Script to run after phys link estab. */ int maxconnect = 0; /* Maximum connect time */ char user[MAXNAMELEN]; /* Username for PAP */ char passwd[MAXSECRETLEN]; /* Password for PAP */ @@ -85,13 +82,25 @@ bool demand = 0; /* do dial-on-demand */ char *ipparam = NULL; /* Extra parameter for ip up/down scripts */ int idle_time_limit = 0; /* Disconnect if idle for this many seconds */ int holdoff = 30; /* # seconds to pause before reconnecting */ +bool holdoff_specified; /* true if a holdoff value has been given */ +int log_to_fd = 1; /* send log messages to this fd too */ +int maxfail = 10; /* max # of unsuccessful connection attempts */ +char linkname[MAXPATHLEN]; /* logical name for link */ +bool tune_kernel; /* may alter kernel settings */ +int connect_delay = 1000; /* wait this many ms after connect script */ +int req_unit = -1; /* requested interface unit */ +bool multilink = 0; /* Enable multilink operation */ +char *bundle_name = NULL; /* bundle name for multilink */ extern option_t auth_options[]; +extern struct stat devstat; -struct option_info connector_info; -struct option_info disconnector_info; +struct option_info initializer_info; +struct option_info connect_script_info; +struct option_info disconnect_script_info; struct option_info welcomer_info; struct option_info devnam_info; +struct option_info ptycommand_info; #ifdef PPP_FILTER struct bpf_program pass_filter;/* Filter program for packets to pass */ @@ -99,33 +108,52 @@ struct bpf_program active_filter; /* Filter program for link-active pkts */ pcap_t pc; /* Fake struct pcap so we can compile expr */ #endif +char *current_option; /* the name of the option being parsed */ +int privileged_option; /* set iff the current option came from root */ +char *option_source; /* string saying where the option came from */ +bool log_to_file; /* log_to_fd is a file opened by us */ +bool log_to_specific_fd; /* log_to_fd was specified by user option */ +bool no_override; /* don't override previously-set options */ + /* * Prototypes */ -static int setdevname __P((char *, int)); +static int setdevname __P((char *)); static int setipaddr __P((char *)); static int setspeed __P((char *)); static int noopt __P((char **)); static int setdomain __P((char **)); static int setnetmask __P((char **)); -static int setxonxoff __P((char **)); static int readfile __P((char **)); static int callfile __P((char **)); static int showversion __P((char **)); static int showhelp __P((char **)); static void usage __P((void)); +static int setlogfile __P((char **)); +#ifdef PLUGIN +static int loadplugin __P((char **)); +#endif #ifdef PPP_FILTER static int setpassfilter __P((char **)); static int setactivefilter __P((char **)); #endif - static option_t *find_option __P((char *name)); static int process_option __P((option_t *, char **)); static int n_arguments __P((option_t *)); static int number_option __P((char *, u_int32_t *, int)); +/* + * Structure to store extra lists of options. + */ +struct option_list { + option_t *options; + struct option_list *next; +}; + +static struct option_list *extra_options = NULL; + /* * Valid arguments. */ @@ -146,59 +174,72 @@ option_t general_options[] = { "Set time in seconds before retrying connection" }, { "idle", o_int, &idle_time_limit, "Set time in seconds before disconnecting idle link" }, - { "lock", o_bool, &lockflag, - "Lock serial device with UUCP-style lock file", 1 }, - { "-all", o_special_noarg, noopt, + { "-all", o_special_noarg, (void *)noopt, "Don't request/allow any LCP or IPCP options (useless)" }, - { "connect", o_string, &connector, - "A program to set up a connection", - OPT_A2INFO | OPT_PRIVFIX, &connector_info }, - { "disconnect", o_string, &disconnector, - "Program to disconnect serial device", - OPT_A2INFO | OPT_PRIVFIX, &disconnector_info }, - { "welcome", o_string, &welcomer, - "Script to welcome client", - OPT_A2INFO | OPT_PRIVFIX, &welcomer_info }, { "maxconnect", o_int, &maxconnect, "Set connection time limit", OPT_LLIMIT|OPT_NOINCR|OPT_ZEROINF }, - { "crtscts", o_int, &crtscts, - "Set hardware (RTS/CTS) flow control", OPT_NOARG|OPT_VAL(1) }, - { "nocrtscts", o_int, &crtscts, - "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) }, - { "-crtscts", o_int, &crtscts, - "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) }, - { "cdtrcts", o_int, &crtscts, - "Set alternate hardware (DTR/CTS) flow control", OPT_NOARG|OPT_VAL(2) }, - { "nocdtrcts", o_int, &crtscts, - "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) }, - { "xonxoff", o_special_noarg, setxonxoff, - "Set software (XON/XOFF) flow control" }, - { "domain", o_special, setdomain, + { "domain", o_special, (void *)setdomain, "Add given domain name to hostname" }, { "mtu", o_int, &lcp_allowoptions[0].mru, "Set our MTU", OPT_LIMITS, NULL, MAXMRU, MINMRU }, - { "netmask", o_special, setnetmask, + { "netmask", o_special, (void *)setnetmask, "set netmask" }, - { "modem", o_bool, &modem, - "Use modem control lines", 1 }, - { "local", o_bool, &modem, - "Don't use modem control lines" }, - { "file", o_special, readfile, - "Take options from a file" }, - { "call", o_special, callfile, - "Take options from a privileged file" }, + { "file", o_special, (void *)readfile, + "Take options from a file", OPT_PREPASS }, + { "call", o_special, (void *)callfile, + "Take options from a privileged file", OPT_PREPASS }, { "persist", o_bool, &persist, "Keep on reopening connection after close", 1 }, { "nopersist", o_bool, &persist, "Turn off persist option" }, { "demand", o_bool, &demand, - "Dial on demand", 1, &persist }, - { "--version", o_special_noarg, showversion, + "Dial on demand", OPT_INITONLY | 1, &persist }, + { "--version", o_special_noarg, (void *)showversion, "Show version number" }, - { "--help", o_special_noarg, showhelp, + { "--help", o_special_noarg, (void *)showhelp, "Show brief listing of options" }, - { "-h", o_special_noarg, showhelp, + { "-h", o_special_noarg, (void *)showhelp, "Show brief listing of options" }, + { "logfd", o_int, &log_to_fd, + "Send log messages to this file descriptor", + 0, &log_to_specific_fd }, + { "logfile", o_special, (void *)setlogfile, + "Append log messages to this file" }, + { "nolog", o_int, &log_to_fd, + "Don't send log messages to any file", + OPT_NOARG | OPT_VAL(-1) }, + { "nologfd", o_int, &log_to_fd, + "Don't send log messages to any file descriptor", + OPT_NOARG | OPT_VAL(-1) }, + { "linkname", o_string, linkname, + "Set logical name for link", + OPT_PRIV|OPT_STATIC, NULL, MAXPATHLEN }, + { "maxfail", o_int, &maxfail, + "Maximum number of unsuccessful connection attempts to allow" }, + { "ktune", o_bool, &tune_kernel, + "Alter kernel settings as necessary", 1 }, + { "noktune", o_bool, &tune_kernel, + "Don't alter kernel settings", 0 }, + { "connect-delay", o_int, &connect_delay, + "Maximum time (in ms) to wait after connect script finishes" }, + { "unit", o_int, &req_unit, + "PPP interface unit number to use if possible", OPT_LLIMIT, 0, 0 }, +#ifdef HAVE_MULTILINK + { "multilink", o_bool, &multilink, + "Enable multilink operation", 1 }, + { "nomultilink", o_bool, &multilink, + "Disable multilink operation", 0 }, + { "mp", o_bool, &multilink, + "Enable multilink operation", 1 }, + { "nomp", o_bool, &multilink, + "Disable multilink operation", 0 }, + { "bundle", o_string, &bundle_name, + "Bundle name for multilink" }, +#endif /* HAVE_MULTILINK */ +#ifdef PLUGIN + { "plugin", o_special, (void *)loadplugin, + "Load a plug-in module into pppd", OPT_PRIV }, +#endif #ifdef PPP_FILTER { "pdebug", o_int, &dflag, @@ -273,7 +314,7 @@ parse_args(argc, argv) /* * Maybe a tty name, speed or IP address? */ - if ((ret = setdevname(arg, 0)) == 0 + if ((ret = setdevname(arg)) == 0 && (ret = setspeed(arg)) == 0 && (ret = setipaddr(arg)) == 0) { option_error("unrecognized option '%s'", arg); @@ -286,9 +327,10 @@ parse_args(argc, argv) return 1; } +#if 0 /* * scan_args - scan the command line arguments to get the tty name, - * if specified. + * if specified. Also checks whether the notty or pty option was given. */ void scan_args(argc, argv) @@ -303,6 +345,9 @@ scan_args(argc, argv) arg = *argv++; --argc; + if (strcmp(arg, "notty") == 0 || strcmp(arg, "pty") == 0) + using_pty = 1; + /* Skip options and their arguments */ opt = find_option(arg); if (opt != NULL) { @@ -316,6 +361,7 @@ scan_args(argc, argv) (void) setdevname(arg, 1); } } +#endif /* * options_from_file - Read a string of options from a file, @@ -329,7 +375,7 @@ options_from_file(filename, must_exist, check_prot, priv) int priv; { FILE *f; - int i, newline, ret; + int i, newline, ret, err; option_t *opt; int oldpriv; char *oldsource; @@ -340,11 +386,16 @@ options_from_file(filename, must_exist, check_prot, priv) if (check_prot) seteuid(getuid()); f = fopen(filename, "r"); + err = errno; if (check_prot) seteuid(0); if (f == NULL) { - if (!must_exist && errno == ENOENT) + errno = err; + if (!must_exist) { + if (err != ENOENT && err != ENOTDIR) + warn("Warning: can't open options file %s: %m", filename); return 1; + } option_error("Can't open options file %s: %m", filename); return 0; } @@ -373,6 +424,11 @@ options_from_file(filename, must_exist, check_prot, priv) argv[i] = args[i]; } current_option = cmd; + if ((opt->flags & OPT_DEVEQUIV) && no_override) { + option_error("the %s option may not be used in the %s file", + cmd, filename); + goto err; + } if (!process_option(opt, argv)) goto err; continue; @@ -381,7 +437,7 @@ options_from_file(filename, must_exist, check_prot, priv) /* * Maybe a tty name, speed or IP address? */ - if ((i = setdevname(cmd, 0)) == 0 + if ((i = setdevname(cmd)) == 0 && (i = setspeed(cmd)) == 0 && (i = setipaddr(cmd)) == 0) { option_error("In file %s: unrecognized option '%s'", @@ -429,6 +485,9 @@ options_from_user() /* * options_for_tty - See if an options file exists for the serial * device, and if so, interpret options from it. + * We only allow the per-tty options file to override anything from + * the command line if it is something that the user can't override + * once it has been set by root. */ int options_for_tty() @@ -440,7 +499,7 @@ options_for_tty() dev = devnam; if (strncmp(dev, "/dev/", 5) == 0) dev += 5; - if (strcmp(dev, "tty") == 0) + if (dev[0] == 0 || strcmp(dev, "tty") == 0) return 1; /* don't look for /etc/ppp/options.tty */ pl = strlen(_PATH_TTYOPT) + strlen(dev) + 1; path = malloc(pl); @@ -451,11 +510,71 @@ options_for_tty() for (p = path + strlen(_PATH_TTYOPT); *p != 0; ++p) if (*p == '/') *p = '.'; + no_override = 1; ret = options_from_file(path, 0, 0, 1); + no_override = 0; free(path); return ret; } +/* + * options_from_list - process a string of options in a wordlist. + */ +int +options_from_list(w, priv) + struct wordlist *w; + int priv; +{ + char *argv[MAXARGS]; + option_t *opt; + int i, ret = 0; + + privileged_option = priv; + option_source = "secrets file"; + + while (w != NULL) { + /* + * First see if it's a command. + */ + opt = find_option(w->word); + if (opt != NULL) { + int n = n_arguments(opt); + struct wordlist *w0 = w; + for (i = 0; i < n; ++i) { + w = w->next; + if (w == NULL) { + option_error( + "In secrets file: too few parameters for option '%s'", + w0->word); + goto err; + } + argv[i] = w->word; + } + current_option = w0->word; + if (!process_option(opt, argv)) + goto err; + continue; + } + + /* + * Maybe a tty name, speed or IP address? + */ + if ((i = setdevname(w->word)) == 0 + && (i = setspeed(w->word)) == 0 + && (i = setipaddr(w->word)) == 0) { + option_error("In secrets file: unrecognized option '%s'", + w->word); + goto err; + } + if (i < 0) /* error */ + goto err; + } + ret = 1; + +err: + return ret; +} + /* * find_option - scan the option lists for the various protocols * looking for an entry with the given name. @@ -466,8 +585,13 @@ find_option(name) char *name; { option_t *opt; + struct option_list *list; int i; + for (list = extra_options; list != NULL; list = list->next) + for (opt = list->options; opt->name != NULL; ++opt) + if (strcmp(name, opt->name) == 0) + return opt; for (opt = general_options; opt->name != NULL; ++opt) if (strcmp(name, opt->name) == 0) return opt; @@ -495,6 +619,18 @@ process_option(opt, argv) char *sv; int (*parser) __P((char **)); + if (no_override && (opt->flags & OPT_SEENIT)) { + struct option_info *ip = (struct option_info *) opt->addr2; + if (!(privileged && (opt->flags & OPT_PRIVFIX))) + return 1; + if (!ip || ip->priv) + return 1; + warn("%s option from per-tty file overrides command line", opt->name); + } + if ((opt->flags & OPT_INITONLY) && phase != PHASE_INITIALIZE) { + option_error("it's too late to use the %s option", opt->name); + return 0; + } if ((opt->flags & OPT_PRIV) && !privileged_option) { option_error("using the %s option requires root privilege", opt->name); return 0; @@ -510,6 +646,7 @@ process_option(opt, argv) return 0; } } + opt->flags |= OPT_SEENIT; switch (opt->type) { case o_bool: @@ -618,6 +755,23 @@ n_arguments(opt) || (opt->flags & OPT_NOARG))? 0: 1; } +/* + * add_options - add a list of options to the set we grok. + */ +void +add_options(opt) + option_t *opt; +{ + struct option_list *list; + + list = malloc(sizeof(*list)); + if (list == 0) + novm("option list entry"); + list->options = opt; + list->next = extra_options; + extra_options = list; +} + /* * usage - print out a message telling how to use the program. */ @@ -669,7 +823,7 @@ option_error __V((char *fmt, ...)) va_list args; char buf[256]; -#if __STDC__ +#if defined(__STDC__) va_start(args, fmt); #else char *fmt; @@ -1055,16 +1209,6 @@ callfile(argv) } #ifdef PPP_FILTER -/* - * setpdebug - Set libpcap debugging level. - */ -static int -setpdebug(argv) - char **argv; -{ - return int_option(*argv, &dflag); -} - /* * setpassfilter - Set the pass filter for packets */ @@ -1145,10 +1289,13 @@ setspeed(arg) char *ptr; int spd; + if (no_override && inspeed != 0) + return 1; spd = strtol(arg, &ptr, 0); if (ptr == arg || *ptr != 0 || spd == 0) return 0; - inspeed = spd; + if (!no_override || inspeed == 0) + inspeed = spd; return 1; } @@ -1157,9 +1304,8 @@ setspeed(arg) * setdevname - Set the device name. */ static int -setdevname(cp, quiet) +setdevname(cp) char *cp; - int quiet; { struct stat statbuf; char dev[MAXPATHLEN]; @@ -1174,22 +1320,35 @@ setdevname(cp, quiet) } /* - * Check if there is a device by this name. + * Check if there is a character device by this name. */ if (stat(cp, &statbuf) < 0) { - if (errno == ENOENT || quiet) + if (errno == ENOENT) return 0; option_error("Couldn't stat %s: %m", cp); return -1; } + if (!S_ISCHR(statbuf.st_mode)) { + option_error("%s is not a character device", cp); + return -1; + } + + if (phase != PHASE_INITIALIZE) { + option_error("device name cannot be changed after initialization"); + return -1; + } + if (no_override) { + option_error("per-tty options file may not specify device name"); + return -1; + } if (devnam_info.priv && !privileged_option) { - if (!quiet) - option_error("device name cannot be overridden"); + option_error("device name cannot be overridden"); return -1; } strlcpy(devnam, cp, sizeof(devnam)); + devstat = statbuf; default_device = 0; devnam_info.priv = privileged_option; devnam_info.source = option_source; @@ -1209,7 +1368,8 @@ setipaddr(arg) char *colon; u_int32_t local, remote; ipcp_options *wo = &ipcp_wantoptions[0]; - + static int seen_local = 0, seen_remote = 0; + /* * IP address pair separated by ":". */ @@ -1219,9 +1379,9 @@ setipaddr(arg) /* * If colon first character, then no local addr. */ - if (colon != arg) { + if (colon != arg && !(no_override && seen_local)) { *colon = '\0'; - if ((local = inet_addr(arg)) == -1) { + if ((local = inet_addr(arg)) == (u_int32_t) -1) { if ((hp = gethostbyname(arg)) == NULL) { option_error("unknown host: %s", arg); return -1; @@ -1236,13 +1396,14 @@ setipaddr(arg) if (local != 0) wo->ouraddr = local; *colon = ':'; + seen_local = 1; } /* * If colon last character, then no remote addr. */ - if (*++colon != '\0') { - if ((remote = inet_addr(colon)) == -1) { + if (*++colon != '\0' && !(no_override && seen_remote)) { + if ((remote = inet_addr(colon)) == (u_int32_t) -1) { if ((hp = gethostbyname(colon)) == NULL) { option_error("unknown host: %s", colon); return -1; @@ -1258,6 +1419,7 @@ setipaddr(arg) } if (remote != 0) wo->hisaddr = remote; + seen_remote = 1; } return 1; @@ -1271,54 +1433,113 @@ static int setnetmask(argv) char **argv; { - u_int32_t mask, b; - int n, ok; - char *p, *endp; + u_int32_t mask; + int n; + char *p; /* * Unfortunately, if we use inet_addr, we can't tell whether * a result of all 1s is an error or a valid 255.255.255.255. */ p = *argv; - ok = 0; - mask = 0; + n = parse_dotted_ip(p, &mask); + + mask = htonl(mask); + + if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) { + option_error("invalid netmask value '%s'", *argv); + return 0; + } + + netmask = mask; + return (1); +} + +int +parse_dotted_ip(p, vp) + char *p; + u_int32_t *vp; +{ + int n; + u_int32_t v, b; + char *endp, *p0 = p; + + v = 0; for (n = 3;; --n) { b = strtoul(p, &endp, 0); if (endp == p) - break; - if (b < 0 || b > 255) { - if (n == 3) { - /* accept e.g. 0xffffff00 */ - p = endp; - mask = b; - } - break; + return 0; + if (b > 255) { + if (n < 3) + return 0; + /* accept e.g. 0xffffff00 */ + *vp = b; + return endp - p0; } - mask |= b << (n * 8); + v |= b << (n * 8); p = endp; - if (*p != '.' || n == 0) + if (n == 0) break; + if (*p != '.') + return 0; ++p; } + *vp = v; + return p - p0; +} - mask = htonl(mask); +static int +setlogfile(argv) + char **argv; +{ + int fd, err; - if (*p != 0 || (netmask & ~mask) != 0) { - option_error("invalid netmask value '%s'", *argv); + if (!privileged_option) + seteuid(getuid()); + fd = open(*argv, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0644); + if (fd < 0 && errno == EEXIST) + fd = open(*argv, O_WRONLY | O_APPEND); + err = errno; + if (!privileged_option) + seteuid(0); + if (fd < 0) { + errno = err; + option_error("Can't open log file %s: %m", *argv); return 0; } - - netmask = mask; - return (1); + if (log_to_file && log_to_fd >= 0) + close(log_to_fd); + log_to_fd = fd; + log_to_file = 1; + return 1; } +#ifdef PLUGIN static int -setxonxoff(argv) +loadplugin(argv) char **argv; { - lcp_wantoptions[0].asyncmap |= 0x000A0000; /* escape ^S and ^Q */ - lcp_wantoptions[0].neg_asyncmap = 1; - - crtscts = -2; - return (1); + char *arg = *argv; + void *handle; + const char *err; + void (*init) __P((void)); + + handle = dlopen(arg, RTLD_GLOBAL | RTLD_NOW); + if (handle == 0) { + err = dlerror(); + if (err != 0) + option_error("%s", err); + option_error("Couldn't load plugin %s", arg); + return 0; + } + init = (void (*)(void))dlsym(handle, "plugin_init"); + if (init == 0) { + option_error("%s has no initialization entry point", arg); + dlclose(handle); + return 0; + } + info("Plugin %s loaded.", arg); + (*init)(); + return 1; } +#endif /* PLUGIN */