From d0950adfd65d0510424b43c31f19d2f1d8d277a4 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Tue, 30 Nov 1999 22:12:50 +0000 Subject: Added port of ppp-2.3.5 from Tomasz Domin of ComArch SA. Tomasz only tested this on the mpc823. The official site for the original source for this PPP implementation is: ftp://cs.anu.edu.au/pub/software/ppp NOTE: As of 11/30/1999, the current version of this source is 2.3.10. --- cpukit/libnetworking/Makefile.am | 2 +- cpukit/libnetworking/net/ppp_defs.h | 183 ++++ cpukit/libnetworking/sys/syslog.h | 5 +- cpukit/pppd/README | 13 + cpukit/pppd/STATUS | 27 + cpukit/pppd/auth.c | 843 ++++++++++++++++ cpukit/pppd/cbcp.c | 431 ++++++++ cpukit/pppd/cbcp.h | 26 + cpukit/pppd/ccp.c | 1117 +++++++++++++++++++++ cpukit/pppd/ccp.h | 48 + cpukit/pppd/chap.c | 872 ++++++++++++++++ cpukit/pppd/chap.h | 124 +++ cpukit/pppd/chap_ms.c | 335 +++++++ cpukit/pppd/chap_ms.h | 33 + cpukit/pppd/chat.c | 1004 +++++++++++++++++++ cpukit/pppd/demand.c | 348 +++++++ cpukit/pppd/fsm.c | 798 +++++++++++++++ cpukit/pppd/fsm.h | 144 +++ cpukit/pppd/ipcp.c | 1506 ++++++++++++++++++++++++++++ cpukit/pppd/ipcp.h | 70 ++ cpukit/pppd/ipxcp.c | 1399 ++++++++++++++++++++++++++ cpukit/pppd/ipxcp.h | 71 ++ cpukit/pppd/lcp.c | 1858 +++++++++++++++++++++++++++++++++++ cpukit/pppd/lcp.h | 88 ++ cpukit/pppd/magic.c | 86 ++ cpukit/pppd/magic.h | 23 + cpukit/pppd/md4.c | 298 ++++++ cpukit/pppd/md4.h | 64 ++ cpukit/pppd/md5.c | 306 ++++++ cpukit/pppd/md5.h | 58 ++ cpukit/pppd/options.c | 393 ++++++++ cpukit/pppd/patchlevel.h | 6 + cpukit/pppd/pathnames.h | 30 + cpukit/pppd/pppd.h | 497 ++++++++++ cpukit/pppd/upap.c | 619 ++++++++++++ cpukit/pppd/upap.h | 87 ++ 36 files changed, 13809 insertions(+), 3 deletions(-) create mode 100644 cpukit/libnetworking/net/ppp_defs.h create mode 100644 cpukit/pppd/README create mode 100644 cpukit/pppd/STATUS create mode 100644 cpukit/pppd/auth.c create mode 100644 cpukit/pppd/cbcp.c create mode 100644 cpukit/pppd/cbcp.h create mode 100644 cpukit/pppd/ccp.c create mode 100644 cpukit/pppd/ccp.h create mode 100644 cpukit/pppd/chap.c create mode 100644 cpukit/pppd/chap.h create mode 100644 cpukit/pppd/chap_ms.c create mode 100644 cpukit/pppd/chap_ms.h create mode 100644 cpukit/pppd/chat.c create mode 100644 cpukit/pppd/demand.c create mode 100644 cpukit/pppd/fsm.c create mode 100644 cpukit/pppd/fsm.h create mode 100644 cpukit/pppd/ipcp.c create mode 100644 cpukit/pppd/ipcp.h create mode 100644 cpukit/pppd/ipxcp.c create mode 100644 cpukit/pppd/ipxcp.h create mode 100644 cpukit/pppd/lcp.c create mode 100644 cpukit/pppd/lcp.h create mode 100644 cpukit/pppd/magic.c create mode 100644 cpukit/pppd/magic.h create mode 100644 cpukit/pppd/md4.c create mode 100644 cpukit/pppd/md4.h create mode 100644 cpukit/pppd/md5.c create mode 100644 cpukit/pppd/md5.h create mode 100644 cpukit/pppd/options.c create mode 100644 cpukit/pppd/patchlevel.h create mode 100644 cpukit/pppd/pathnames.h create mode 100644 cpukit/pppd/pppd.h create mode 100644 cpukit/pppd/upap.c create mode 100644 cpukit/pppd/upap.h (limited to 'cpukit') diff --git a/cpukit/libnetworking/Makefile.am b/cpukit/libnetworking/Makefile.am index 6ca714c5de..3ecfea31bc 100644 --- a/cpukit/libnetworking/Makefile.am +++ b/cpukit/libnetworking/Makefile.am @@ -10,7 +10,7 @@ POSIX_SUBDIRS = rtems_webserver endif SUBDIRS = include kern lib libc net netinet nfs rtems rtems_servers \ -$(POSIX_SUBDIRS) wrapup +pppd $(POSIX_SUBDIRS) wrapup EXTRA_DIST = \ CHANGELOG \ diff --git a/cpukit/libnetworking/net/ppp_defs.h b/cpukit/libnetworking/net/ppp_defs.h new file mode 100644 index 0000000000..89c62af6cb --- /dev/null +++ b/cpukit/libnetworking/net/ppp_defs.h @@ -0,0 +1,183 @@ +/* $Id$ */ + +/* + * ppp_defs.h - PPP definitions. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +#ifndef _PPP_DEFS_H_ +#define _PPP_DEFS_H_ + +/* + * The basic PPP frame. + */ +#define PPP_HDRLEN 4 /* octets for standard ppp header */ +#define PPP_FCSLEN 2 /* octets for FCS */ + +/* + * Packet sizes + * + * Note - lcp shouldn't be allowed to negotiate stuff outside these + * limits. See lcp.h in the pppd directory. + * (XXX - these constants should simply be shared by lcp.c instead + * of living in lcp.h) + */ +#define PPP_MTU 1500 /* Default MTU (size of Info field) */ +#define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) +#define PPP_MINMTU 64 +#define PPP_MRU 1500 /* default MRU = max length of info field */ +#define PPP_MAXMRU 65000 /* Largest MRU we allow */ +#define PPP_MINMRU 128 + +#define PPP_ADDRESS(p) (((u_char *)(p))[0]) +#define PPP_CONTROL(p) (((u_char *)(p))[1]) +#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3]) + +/* + * Significant octet values. + */ +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_FLAG 0x7e /* Flag Sequence */ +#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ +#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ + +/* + * Protocol field values. + */ +#define PPP_IP 0x21 /* Internet Protocol */ +#define PPP_AT 0x29 /* AppleTalk Protocol */ +#define PPP_IPX 0x2b /* IPX protocol */ +#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ +#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ +#define PPP_IPV6 0x57 /* Internet Protocol Version 6 */ +#define PPP_COMP 0xfd /* compressed packet */ +#define PPP_IPCP 0x8021 /* IP Control Protocol */ +#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */ +#define PPP_IPXCP 0x802b /* IPX Control Protocol */ +#define PPP_CCP 0x80fd /* Compression Control Protocol */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ +#define PPP_PAP 0xc023 /* Password Authentication Protocol */ +#define PPP_LQR 0xc025 /* Link Quality Report protocol */ +#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ +#define PPP_CBCP 0xc029 /* Callback Control Protocol */ + +/* + * Values for FCS calculations. + */ +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +/* + * A 32-bit unsigned integral type. + */ + +#if !defined(__BIT_TYPES_DEFINED__) && !defined(_BITYPES) \ + && !defined(__FreeBSD__) && (NS_TARGET < 40) && !defined(__rtems__) +#ifdef UINT32_T +typedef UINT32_T u_int32_t; +#else +typedef unsigned int u_int32_t; +typedef unsigned short u_int16_t; +#endif +#endif + +/* + * Extended asyncmap - allows any character to be escaped. + */ +typedef u_int32_t ext_accm[8]; + +/* + * What to do with network protocol (NP) packets. + */ +enum NPmode { + NPMODE_PASS, /* pass the packet through */ + NPMODE_DROP, /* silently drop the packet */ + NPMODE_ERROR, /* return an error */ + NPMODE_QUEUE /* save it up for later. */ +}; + +/* + * Statistics. + */ +struct pppstat { + unsigned int ppp_ibytes; /* bytes received */ + unsigned int ppp_ipackets; /* packets received */ + unsigned int ppp_ierrors; /* receive errors */ + unsigned int ppp_obytes; /* bytes sent */ + unsigned int ppp_opackets; /* packets sent */ + unsigned int ppp_oerrors; /* transmit errors */ +}; + +struct vjstat { + unsigned int vjs_packets; /* outbound packets */ + unsigned int vjs_compressed; /* outbound compressed packets */ + unsigned int vjs_searches; /* searches for connection state */ + unsigned int vjs_misses; /* times couldn't find conn. state */ + unsigned int vjs_uncompressedin; /* inbound uncompressed packets */ + unsigned int vjs_compressedin; /* inbound compressed packets */ + unsigned int vjs_errorin; /* inbound unknown type packets */ + unsigned int vjs_tossed; /* inbound packets tossed because of error */ +}; + +struct ppp_stats { + struct pppstat p; /* basic PPP statistics */ + struct vjstat vj; /* VJ header compression statistics */ +}; + +struct compstat { + unsigned int unc_bytes; /* total uncompressed bytes */ + unsigned int unc_packets; /* total uncompressed packets */ + unsigned int comp_bytes; /* compressed bytes */ + unsigned int comp_packets; /* compressed packets */ + unsigned int inc_bytes; /* incompressible bytes */ + unsigned int inc_packets; /* incompressible packets */ + unsigned int ratio; /* recent compression ratio << 8 */ +}; + +struct ppp_comp_stats { + struct compstat c; /* packet compression statistics */ + struct compstat d; /* packet decompression statistics */ +}; + +/* + * The following structure records the time in seconds since + * the last NP packet was sent or received. + */ +struct ppp_idle { + time_t xmit_idle; /* time since last NP packet sent */ + time_t recv_idle; /* time since last NP packet received */ +}; + +#ifndef __P +#ifdef __STDC__ +#define __P(x) x +#else +#define __P(x) () +#endif +#endif + +#endif /* _PPP_DEFS_H_ */ diff --git a/cpukit/libnetworking/sys/syslog.h b/cpukit/libnetworking/sys/syslog.h index 724bee62c7..08a59fa7cc 100644 --- a/cpukit/libnetworking/sys/syslog.h +++ b/cpukit/libnetworking/sys/syslog.h @@ -174,9 +174,10 @@ CODE facilitynames[] = { #define LOG_NOWAIT 0x10 /* don't wait for console forks: DEPRECATED */ #define LOG_PERROR 0x20 /* log to stderr as well */ +/* #ifdef KERNEL -#else /* not KERNEL */ +#else not KERNEL */ #include #include @@ -189,6 +190,6 @@ void syslog __P((int, const char *, ...)); void vsyslog __P((int, const char *, va_list)); __END_DECLS -#endif /* !KERNEL */ +/* #endif !KERNEL */ #endif diff --git a/cpukit/pppd/README b/cpukit/pppd/README new file mode 100644 index 0000000000..f33926e326 --- /dev/null +++ b/cpukit/pppd/README @@ -0,0 +1,13 @@ +# +# $Id$ +# + +This directory contains a port of ppp-2.3.5. The official site for +the original source for this PPP implementation is: + +ftp://cs.anu.edu.au/pub/software/ppp + +NOTE: As of 11/30/1999, the current version of this source is 2.3.10. + +The port was performed by Tomasz Domin of ComArch SA +and has only been tested on the mpc823. diff --git a/cpukit/pppd/STATUS b/cpukit/pppd/STATUS new file mode 100644 index 0000000000..95ef16cefc --- /dev/null +++ b/cpukit/pppd/STATUS @@ -0,0 +1,27 @@ +# +# $Id$ +# + +Overall, this code should still be considered in its early stages. It +works but has some distance to go before it is fully documented and +easily configurable. + ++ Compare the code to the original 2.3.5 and eliminate spurious changes. + ++ Update the code to 2.3.10. + ++ Eliminate items specific to Tomasz' system. In particular, the + code reports status and gets configuration information in a system + specific manner. main.c is particularly guilty of this although + other files suffer from this also. + ++ Find comments in Polish and get Tomasz to translate them. :) + ++ Add netdemo showing configuration and initialization. + ++ Get feature list. + ++ Document dialer setup. + ++ Only modem driver is system specific so there is the possibility + that shareable code exists in it. diff --git a/cpukit/pppd/auth.c b/cpukit/pppd/auth.c new file mode 100644 index 0000000000..f5cff9ac3d --- /dev/null +++ b/cpukit/pppd/auth.c @@ -0,0 +1,843 @@ +/* + * auth.c - PPP authentication and phase control. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +/* static char rcsid[] = "$Id$"; */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#if defined(_PATH_LASTLOG) && defined(_linux_) +#include +#endif + +#include +#include +#include +/* #include */ + +#include "pppd.h" +#include "fsm.h" +#include "lcp.h" +#include "ipcp.h" +#include "upap.h" +#include "chap.h" +#ifdef CBCP_SUPPORT +#include "cbcp.h" +#endif +#include "pathnames.h" + +/* Used for storing a sequence of words. Usually malloced. */ +struct wordlist { + struct wordlist *next; + char word[1]; +}; + +/* Bits in scan_authfile return value */ +#define NONWILD_SERVER 1 +#define NONWILD_CLIENT 2 + +#define ISWILD(word) (word[0] == '*' && word[1] == 0) + +#define FALSE 0 +#define TRUE 1 + +/* The name by which the peer authenticated itself to us. */ +char peer_authname[MAXNAMELEN]; + +/* Records which authentication operations haven't completed yet. */ +static int auth_pending[NUM_PPP]; + +/* Set if we have successfully called plogin() */ +static int logged_in; + +/* Set if we have run the /etc/ppp/auth-up script. */ +static int did_authup; + +/* List of addresses which the peer may use. */ +static struct wordlist *addresses[NUM_PPP]; + +/* Number of network protocols which we have opened. */ +static int num_np_open; + +/* Number of network protocols which have come up. */ +static int num_np_up; + +/* Set if we got the contents of passwd[] from the pap-secrets file. */ +static int passwd_from_file; + +/* Bits in auth_pending[] */ +#define PAP_WITHPEER 1 +#define PAP_PEER 2 +#define CHAP_WITHPEER 4 +#define CHAP_PEER 8 + +extern char *crypt __P((const char *, const char *)); + +/* Prototypes for procedures local to this file. */ + +static void network_phase __P((int)); +static void check_idle __P((void *)); +static void connect_time_expired __P((void *)); +static int plogin __P((char *, char *, char **, int *)); +static void plogout __P((void)); +static int null_login __P((int)); +static int get_pap_passwd __P((char *)); +static int have_pap_secret __P((void)); +static int have_chap_secret __P((char *, char *, u_int32_t)); +static int ip_addr_check __P((u_int32_t, struct wordlist *)); +static int scan_authfile __P((FILE *, char *, char *, u_int32_t, char *, + struct wordlist **, char *)); +static void free_wordlist __P((struct wordlist *)); +static void auth_script __P((char *)); +static void set_allowed_addrs __P((int, struct wordlist *)); + +/* + * An Open on LCP has requested a change from Dead to Establish phase. + * Do what's necessary to bring the physical layer up. + */ +void +link_required(unit) + int unit; +{ +} + +/* + * LCP has terminated the link; go to the Dead phase and take the + * physical layer down. + */ +void +link_terminated(unit) + int unit; +{ + if (phase == PHASE_DEAD) + return; + if (logged_in) + plogout(); + phase = PHASE_DEAD; + syslog(LOG_NOTICE, "Connection terminated."); +} + +/* + * LCP has gone down; it will either die or try to re-establish. + */ +void +link_down(unit) + int unit; +{ + int i; + struct protent *protp; + + did_authup = 0; + for (i = 0; (protp = protocols[i]) != NULL; ++i) { + if (!protp->enabled_flag) + continue; + if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) + (*protp->lowerdown)(unit); + if (protp->protocol < 0xC000 && protp->close != NULL) + (*protp->close)(unit, "LCP down"); + } + num_np_open = 0; + num_np_up = 0; + if (phase != PHASE_DEAD) + phase = PHASE_TERMINATE; +} + +/* + * The link is established. + * Proceed to the Dead, Authenticate or Network phase as appropriate. + */ +void +link_established(unit) + int unit; +{ + int auth; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ho = &lcp_hisoptions[unit]; + int i; + struct protent *protp; + + /* + * Tell higher-level protocols that LCP is up. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol != PPP_LCP && protp->enabled_flag + && protp->lowerup != NULL) + (*protp->lowerup)(unit); + + if (auth_required && !(go->neg_chap || go->neg_upap)) { + /* + * We wanted the peer to authenticate itself, and it refused: + * treat it as though it authenticated with PAP using a username + * of "" and a password of "". If that's not OK, boot it out. + */ + if (!wo->neg_upap || !null_login(unit)) { + lcp_close(unit, "peer refused to authenticate"); + return; + } + } + + phase = PHASE_AUTHENTICATE; + auth = 0; + if (go->neg_chap) { + ChapAuthPeer(unit, our_name, go->chap_mdtype); + auth |= CHAP_PEER; + } else if (go->neg_upap) { + upap_authpeer(unit); + auth |= PAP_PEER; + } + if (ho->neg_chap) { + ChapAuthWithPeer(unit, user, ho->chap_mdtype); + auth |= CHAP_WITHPEER; + } else if (ho->neg_upap) { + if (passwd[0] == 0) { + passwd_from_file = 1; + get_pap_passwd(passwd); + } + upap_authwithpeer(unit, user, passwd); + auth |= PAP_WITHPEER; + } + auth_pending[unit] = auth; + + if (!auth) + network_phase(unit); +} + +/* + * Proceed to the network phase. + */ +static void +network_phase(unit) + int unit; +{ + int i; + struct protent *protp; + lcp_options *go = &lcp_gotoptions[unit]; + + /* + * If the peer had to authenticate, run the auth-up script now. + */ + if ((go->neg_chap || go->neg_upap) && !did_authup) { + auth_script(_PATH_AUTHUP); + did_authup = 1; + } + +#ifdef CBCP_SUPPORT + /* + * If we negotiated callback, do it now. + */ + if (go->neg_cbcp) { + phase = PHASE_CALLBACK; + (*cbcp_protent.open)(unit); + return; + } +#endif + + phase = PHASE_NETWORK; + + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol < 0xC000 && protp->enabled_flag + && protp->open != NULL) { + (*protp->open)(unit); + if (protp->protocol != PPP_CCP) + ++num_np_open; + } + + if (num_np_open == 0) + /* nothing to do */ + lcp_close(0, "No network protocols running"); +} + +/* + * The peer has failed to authenticate himself using `protocol'. + */ +void +auth_peer_fail(unit, protocol) + int unit, protocol; +{ + /* + * Authentication failure: take the link down + */ + lcp_close(unit, "Authentication failed"); +} + +/* + * The peer has been successfully authenticated using `protocol'. + */ +void +auth_peer_success(unit, protocol, name, namelen) + int unit, protocol; + char *name; + int namelen; +{ + int bit; + + switch (protocol) { + case PPP_CHAP: + bit = CHAP_PEER; + break; + case PPP_PAP: + bit = PAP_PEER; + break; + default: + syslog(LOG_WARNING, "auth_peer_success: unknown protocol %x", + protocol); + return; + } + + /* + * Save the authenticated name of the peer for later. + */ + if (namelen > sizeof(peer_authname) - 1) + namelen = sizeof(peer_authname) - 1; + BCOPY(name, peer_authname, namelen); + peer_authname[namelen] = 0; + /* + * If there is no more authentication still to be done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~bit) == 0) + network_phase(unit); +} + +/* + * We have failed to authenticate ourselves to the peer using `protocol'. + */ +void +auth_withpeer_fail(unit, protocol) + int unit, protocol; +{ + if (passwd_from_file) + BZERO(passwd, MAXSECRETLEN); + /* + * We've failed to authenticate ourselves to our peer. + * He'll probably take the link down, and there's not much + * we can do except wait for that. + */ +} + +/* + * We have successfully authenticated ourselves with the peer using `protocol'. + */ +void +auth_withpeer_success(unit, protocol) + int unit, protocol; +{ + int bit; + + switch (protocol) { + case PPP_CHAP: + bit = CHAP_WITHPEER; + break; + case PPP_PAP: + if (passwd_from_file) + BZERO(passwd, MAXSECRETLEN); + bit = PAP_WITHPEER; + break; + default: + + bit = 0; + } + + /* + * If there is no more authentication still being done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~bit) == 0) + network_phase(unit); +} + + +/* + * np_up - a network protocol has come up. + */ +void +np_up(unit, proto) + int unit, proto; +{ + if (num_np_up == 0) { + /* + * At this point we consider that the link has come up successfully. + */ + need_holdoff = 0; + + if (idle_time_limit > 0) + TIMEOUT(check_idle, NULL, idle_time_limit); + + /* + * Set a timeout to close the connection once the maximum + * connect time has expired. + */ + if (maxconnect > 0) + TIMEOUT(connect_time_expired, 0, maxconnect); + + /* + * Detach now, if the updetach option was given. + */ + if (nodetach == -1) + detach(); + } + ++num_np_up; +} + +/* + * np_down - a network protocol has gone down. + */ +void +np_down(unit, proto) + int unit, proto; +{ + if (--num_np_up == 0 && idle_time_limit > 0) { + UNTIMEOUT(check_idle, NULL); + } +} + +/* + * np_finished - a network protocol has finished using the link. + */ +void +np_finished(unit, proto) + int unit, proto; +{ + if (--num_np_open <= 0) { + /* no further use for the link: shut up shop. */ + lcp_close(0, "No network protocols running"); + } +} + +/* + * check_idle - check whether the link has been idle for long + * enough that we can shut it down. + */ +static void +check_idle(arg) + void *arg; +{ + struct ppp_idle idle; + time_t itime; + + if (!get_idle_time(0, &idle)) + return; + itime = MIN(idle.xmit_idle, idle.recv_idle); + if (itime >= idle_time_limit) { + /* link is idle: shut it down. */ + syslog(LOG_INFO, "Terminating connection due to lack of activity."); + lcp_close(0, "Link inactive"); + } else { + TIMEOUT(check_idle, NULL, idle_time_limit - itime); + } +} + +/* + * connect_time_expired - log a message and close the connection. + */ +static void +connect_time_expired(arg) + void *arg; +{ + syslog(LOG_INFO, "Connect time expired"); + lcp_close(0, "Connect time expired"); /* Close connection */ +} + +/* + * auth_check_options - called to check authentication options. + */ +void +auth_check_options() +{ + lcp_options *wo = &lcp_wantoptions[0]; + int can_auth; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u_int32_t remote; + + /* Default our_name to hostname, and user to our_name */ + if (our_name[0] == 0 || usehostname) + strcpy(our_name, hostname); + if (user[0] == 0) + strcpy(user, our_name); + + /* If authentication is required, ask peer for CHAP or PAP. */ + if (auth_required && !wo->neg_chap && !wo->neg_upap) { + wo->neg_chap = 1; + wo->neg_upap = 1; + } + + /* + * Check whether we have appropriate secrets to use + * to authenticate the peer. + */ + can_auth = wo->neg_upap && (uselogin || have_pap_secret()); + if (!can_auth && wo->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + can_auth = have_chap_secret(remote_name, our_name, remote); + } + + if (auth_required && !can_auth) { + option_error("peer authentication required but no suitable secret(s) found\n"); + if (remote_name[0] == 0) + option_error("for authenticating any peer to us (%s)\n", our_name); + else + option_error("for authenticating peer %s to us (%s)\n", + remote_name, our_name); + exit(1); + } + + /* + * Check whether the user tried to override certain values + * set by root. + */ + if (!auth_required && auth_req_info.priv > 0) { + if (!default_device && devnam_info.priv == 0) { + option_error("can't override device name when noauth option used"); + exit(1); + } + if ((connector != NULL && connector_info.priv == 0) + || (disconnector != NULL && disconnector_info.priv == 0) + || (welcomer != NULL && welcomer_info.priv == 0)) { + option_error("can't override connect, disconnect or welcome"); + option_error("option values when noauth option used"); + exit(1); + } + } +} + +/* + * auth_reset - called when LCP is starting negotiations to recheck + * authentication options, i.e. whether we have appropriate secrets + * to use for authenticating ourselves and/or the peer. + */ +void +auth_reset(unit) + int unit; +{ + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ao = &lcp_allowoptions[0]; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u_int32_t remote; + + ao->neg_upap = !refuse_pap /*&& (passwd[0] != 0 || get_pap_passwd(NULL))*/; + ao->neg_chap = !refuse_chap + && have_chap_secret(user, remote_name, (u_int32_t)0); + + if (go->neg_upap && !uselogin && !have_pap_secret()) + go->neg_upap = 0; + if (go->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + if (!have_chap_secret(remote_name, our_name, remote)) + go->neg_chap = 0; + } +} + + +/* + * check_passwd - Check the user name and passwd against the PAP secrets + * file. If requested, also check against the system password database, + * and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Authentication failed. + * UPAP_AUTHACK: Authentication succeeded. + * In either case, msg points to an appropriate message. + */ +int +check_passwd(unit, auser, userlen, apasswd, passwdlen, msg, msglen) + int unit; + char *auser; + int userlen; + char *apasswd; + int passwdlen; + char **msg; + int *msglen; +{ + + + return UPAP_AUTHNAK; +} + + +/* + * plogin - Check the user name and password against the system + * password database, and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Login failed. + * UPAP_AUTHACK: Login succeeded. + * In either case, msg points to an appropriate message. + */ + +static int +plogin(user, passwd, msg, msglen) + char *user; + char *passwd; + char **msg; + int *msglen; +{ + syslog(LOG_INFO, "user %s logged in", user); + logged_in = TRUE; + + return (UPAP_AUTHACK); +} + +/* + * plogout - Logout the user. + */ +static void +plogout() +{ + + logged_in = FALSE; +} + + +/* + * null_login - Check if a username of "" and a password of "" are + * acceptable, and iff so, set the list of acceptable IP addresses + * and return 1. + */ +static int +null_login(unit) + int unit; +{ + return 1; +} + + +/* + * get_pap_passwd - get a password for authenticating ourselves with + * our peer using PAP. Returns 1 on success, 0 if no suitable password + * could be found. + */ + +static int +get_pap_passwd(passwd) + char *passwd; +{ +#if 0 +/* XXX PPPConfiguration */ + GlobalSystemStatus *stat; + stat=LockSTBSystemParam(); + strncpy(passwd, stat->PPP_Password, MAXSECRETLEN); + UnlockSTBSystemParam(); +#endif + return 1; +} + + +/* + * have_pap_secret - check whether we have a PAP file with any + * secrets that we could possibly use for authenticating the peer. + */ +static int +have_pap_secret() +{ + return 1; +} + + +/* + * have_chap_secret - check whether we have a CHAP file with a + * secret that we could possibly use for authenticating `client' + * on `server'. Either can be the null string, meaning we don't + * know the identity yet. + */ +static int +have_chap_secret(client, server, remote) + char *client; + char *server; + u_int32_t remote; +{ + + return 1; +} + + +/* + * get_secret - open the CHAP secret file and return the secret + * for authenticating the given client on the given server. + * (We could be either client or server). + */ +int +get_secret(unit, client, server, secret, secret_len, save_addrs) + int unit; + char *client; + char *server; + char *secret; + int *secret_len; + int save_addrs; +{ +#if 0 +/* XXX PPPConfiguration */ + int len; + GlobalSystemStatus *stat; + stat=LockSTBSystemParam(); + len=strlen(stat->PPP_Password); + strcpy( secret,stat->PPP_Password); + UnlockSTBSystemParam(); + + *secret_len = len; +#endif + return 1; +} + +/* + * set_allowed_addrs() - set the list of allowed addresses. + */ +static void +set_allowed_addrs(unit, addrs) + int unit; + struct wordlist *addrs; +{ + +} + +/* + * auth_ip_addr - check whether the peer is authorized to use + * a given IP address. Returns 1 if authorized, 0 otherwise. + */ +int +auth_ip_addr(unit, addr) + int unit; + u_int32_t addr; +{ + return ip_addr_check(addr, addresses[unit]); +} + +static int +ip_addr_check(addr, addrs) + u_int32_t addr; + struct wordlist *addrs; +{ +#if 0 + u_int32_t a, mask, ah; + int accept; + char *ptr_word, *ptr_mask; + struct hostent *hp; + struct netent *np; +#endif + + /* don't allow loopback or multicast address */ + if (bad_ip_adrs(addr)) + return 0; + + if (addrs == NULL) + return !auth_required; /* no addresses authorized */ + + return 1; +} + +/* + * bad_ip_adrs - return 1 if the IP address is one we don't want + * to use, such as an address in the loopback net or a multicast address. + * addr is in network byte order. + */ +int +bad_ip_adrs(addr) + u_int32_t addr; +{ + addr = ntohl(addr); + return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET + || IN_MULTICAST(addr) || IN_BADCLASS(addr); +} + +/* + * check_access - complain if a secret file has too-liberal permissions. + */ +void +check_access(f, filename) + FILE *f; + char *filename; +{ + +} + + +/* + * scan_authfile - Scan an authorization file for a secret suitable + * for authenticating `client' on `server'. The return value is -1 + * if no secret is found, otherwise >= 0. The return value has + * NONWILD_CLIENT set if the secret didn't have "*" for the client, and + * NONWILD_SERVER set if the secret didn't have "*" for the server. + * Any following words on the line (i.e. address authorization + * info) are placed in a wordlist and returned in *addrs. + */ +static int +scan_authfile(f, client, server, ipaddr, secret, addrs, filename) + FILE *f; + char *client; + char *server; + u_int32_t ipaddr; + char *secret; + struct wordlist **addrs; + char *filename; +{ + + return -1; +} + +/* + * free_wordlist - release memory allocated for a wordlist. + */ +static void +free_wordlist(wp) + struct wordlist *wp; +{ + struct wordlist *next; + + while (wp != NULL) { + next = wp->next; + free(wp); + wp = next; + } +} + +/* + * auth_script - execute a script with arguments + * interface-name peer-name real-user tty speed + */ +static void +auth_script(script) + char *script; +{ +} diff --git a/cpukit/pppd/cbcp.c b/cpukit/pppd/cbcp.c new file mode 100644 index 0000000000..147e56a07b --- /dev/null +++ b/cpukit/pppd/cbcp.c @@ -0,0 +1,431 @@ +/* + * cbcp - Call Back Configuration Protocol. + * + * Copyright (c) 1995 Pedro Roque Marques + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Pedro Roque Marques. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +/* static char rcsid[] = "$Id$"; */ +#endif + +#include +#include +#include +#include +#include + +#include "pppd.h" +#include "cbcp.h" +#include "fsm.h" +#include "lcp.h" +#include "ipcp.h" + +/* + * Protocol entry points. + */ +static void cbcp_init __P((int unit)); +static void cbcp_open __P((int unit)); +static void cbcp_lowerup __P((int unit)); +static void cbcp_input __P((int unit, u_char *pkt, int len)); +static void cbcp_protrej __P((int unit)); +static int cbcp_printpkt __P((u_char *pkt, int len, + void (*printer) __P((void *, char *, ...)), + void *arg)); + +struct protent cbcp_protent = { + PPP_CBCP, + cbcp_init, + cbcp_input, + cbcp_protrej, + cbcp_lowerup, + NULL, + cbcp_open, + NULL, + cbcp_printpkt, + NULL, + 0, + "CBCP", + NULL, + NULL, + NULL +}; + +cbcp_state cbcp[NUM_PPP]; + +/* internal prototypes */ + +static void cbcp_recvreq __P((cbcp_state *us, char *pckt, int len)); +static void cbcp_resp __P((cbcp_state *us)); +static void cbcp_up __P((cbcp_state *us)); +static void cbcp_recvack __P((cbcp_state *us, char *pckt, int len)); +static void cbcp_send __P((cbcp_state *us, u_char code, u_char *buf, int len)); + +/* init state */ +static void +cbcp_init(iface) + int iface; +{ + cbcp_state *us; + + us = &cbcp[iface]; + memset(us, 0, sizeof(cbcp_state)); + us->us_unit = iface; + us->us_type |= (1 << CB_CONF_NO); +} + +/* lower layer is up */ +static void +cbcp_lowerup(iface) + int iface; +{ + cbcp_state *us = &cbcp[iface]; + + + syslog(LOG_DEBUG, "cbcp_lowerup"); + syslog(LOG_DEBUG, "want: %d", us->us_type); + + if (us->us_type == CB_CONF_USER) + syslog(LOG_DEBUG, "phone no: %s", us->us_number); +} + +static void +cbcp_open(unit) + int unit; +{ + syslog(LOG_DEBUG, "cbcp_open"); +} + +/* process an incomming packet */ +static void +cbcp_input(unit, inpacket, pktlen) + int unit; + u_char *inpacket; + int pktlen; +{ + u_char *inp; + u_char code, id; + u_short len; + + cbcp_state *us = &cbcp[unit]; + + inp = inpacket; + + if (pktlen < CBCP_MINLEN) { + syslog(LOG_ERR, "CBCP packet is too small"); + return; + } + + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + +#if 0 + if (len > pktlen) { + syslog(LOG_ERR, "CBCP packet: invalid length"); + return; + } +#endif + + len -= CBCP_MINLEN; + + switch(code) { + case CBCP_REQ: + us->us_id = id; + cbcp_recvreq(us, inp, len); + break; + + case CBCP_RESP: + syslog(LOG_DEBUG, "CBCP_RESP received"); + break; + + case CBCP_ACK: + if (id != us->us_id) + syslog(LOG_DEBUG, "id doesn't match: expected %d recv %d", + us->us_id, id); + + cbcp_recvack(us, inp, len); + break; + + default: + break; + } +} + +/* protocol was rejected by foe */ +void cbcp_protrej(int iface) +{ +} + +char *cbcp_codenames[] = { + "Request", "Response", "Ack" +}; + +char *cbcp_optionnames[] = { + "NoCallback", + "UserDefined", + "AdminDefined", + "List" +}; + +/* pretty print a packet */ +static int +cbcp_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int code, opt, id, len, olen, delay; + u_char *pstart; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(cbcp_codenames) / sizeof(char *)) + printer(arg, " %s", cbcp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + + switch (code) { + case CBCP_REQ: + case CBCP_RESP: + case CBCP_ACK: + while(len >= 2) { + GETCHAR(opt, p); + GETCHAR(olen, p); + + if (olen < 2 || olen > len) { + break; + } + + printer(arg, " <"); + len -= olen; + + if (opt >= 1 && opt <= sizeof(cbcp_optionnames) / sizeof(char *)) + printer(arg, " %s", cbcp_optionnames[opt-1]); + else + printer(arg, " option=0x%x", opt); + + if (olen > 2) { + GETCHAR(delay, p); + printer(arg, " delay = %d", delay); + } + + if (olen > 3) { + int addrt; + char str[256]; + + GETCHAR(addrt, p); + memcpy(str, p, olen - 4); + str[olen - 4] = 0; + printer(arg, " number = %s", str); + } + printer(arg, ">"); + break; + } + + default: + break; + } + + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} + +/* received CBCP request */ +static void +cbcp_recvreq(us, pckt, pcktlen) + cbcp_state *us; + char *pckt; + int pcktlen; +{ + u_char type, opt_len, delay, addr_type; + char address[256]; + int len = pcktlen; + + address[0] = 0; + + while (len) { + syslog(LOG_DEBUG, "length: %d", len); + + GETCHAR(type, pckt); + GETCHAR(opt_len, pckt); + + if (opt_len > 2) + GETCHAR(delay, pckt); + + us->us_allowed |= (1 << type); + + switch(type) { + case CB_CONF_NO: + syslog(LOG_DEBUG, "no callback allowed"); + break; + + case CB_CONF_USER: + syslog(LOG_DEBUG, "user callback allowed"); + if (opt_len > 4) { + GETCHAR(addr_type, pckt); + memcpy(address, pckt, opt_len - 4); + address[opt_len - 4] = 0; + if (address[0]) + syslog(LOG_DEBUG, "address: %s", address); + } + break; + + case CB_CONF_ADMIN: + syslog(LOG_DEBUG, "user admin defined allowed"); + break; + + case CB_CONF_LIST: + break; + } + len -= opt_len; + } + + cbcp_resp(us); +} + +static void +cbcp_resp(us) + cbcp_state *us; +{ + u_char cb_type; + u_char buf[256]; + u_char *bufp = buf; + int len = 0; + + cb_type = us->us_allowed & us->us_type; + syslog(LOG_DEBUG, "cbcp_resp cb_type=%d", cb_type); + +#if 0 + if (!cb_type) + lcp_down(us->us_unit); +#endif + + if (cb_type & ( 1 << CB_CONF_USER ) ) { + syslog(LOG_DEBUG, "cbcp_resp CONF_USER"); + PUTCHAR(CB_CONF_USER, bufp); + len = 3 + 1 + strlen(us->us_number) + 1; + PUTCHAR(len , bufp); + PUTCHAR(5, bufp); /* delay */ + PUTCHAR(1, bufp); + BCOPY(us->us_number, bufp, strlen(us->us_number) + 1); + cbcp_send(us, CBCP_RESP, buf, len); + return; + } + + if (cb_type & ( 1 << CB_CONF_ADMIN ) ) { + syslog(LOG_DEBUG, "cbcp_resp CONF_ADMIN"); + PUTCHAR(CB_CONF_ADMIN, bufp); + len = 3 + 1; + PUTCHAR(len , bufp); + PUTCHAR(5, bufp); /* delay */ + PUTCHAR(0, bufp); + cbcp_send(us, CBCP_RESP, buf, len); + return; + } + + if (cb_type & ( 1 << CB_CONF_NO ) ) { + syslog(LOG_DEBUG, "cbcp_resp CONF_NO"); + PUTCHAR(CB_CONF_NO, bufp); + len = 3; + PUTCHAR(len , bufp); + PUTCHAR(0, bufp); + cbcp_send(us, CBCP_RESP, buf, len); + (*ipcp_protent.open)(us->us_unit); + return; + } +} + +static void +cbcp_send(us, code, buf, len) + cbcp_state *us; + u_char code; + u_char *buf; + int len; +{ + u_char *outp; + int outlen; + + outp = outpacket_buf; + + outlen = 4 + len; + + MAKEHEADER(outp, PPP_CBCP); + + PUTCHAR(code, outp); + PUTCHAR(us->us_id, outp); + PUTSHORT(outlen, outp); + + if (len) + BCOPY(buf, outp, len); + + output(us->us_unit, outpacket_buf, outlen + PPP_HDRLEN); +} + +static void +cbcp_recvack(us, pckt, len) + cbcp_state *us; + char *pckt; + int len; +{ + u_char type, delay, addr_type; + int opt_len; + char address[256]; + + if (len) { + GETCHAR(type, pckt); + GETCHAR(opt_len, pckt); + + if (opt_len > 2) + GETCHAR(delay, pckt); + + if (opt_len > 4) { + GETCHAR(addr_type, pckt); + memcpy(address, pckt, opt_len - 4); + address[opt_len - 4] = 0; + if (address[0]) + syslog(LOG_DEBUG, "peer will call: %s", address); + } + } + + cbcp_up(us); +} + +extern int persist; + +/* ok peer will do callback */ +static void +cbcp_up(us) + cbcp_state *us; +{ + persist = 0; + lcp_close(0, "Call me back, please"); +} diff --git a/cpukit/pppd/cbcp.h b/cpukit/pppd/cbcp.h new file mode 100644 index 0000000000..c2ab3f6899 --- /dev/null +++ b/cpukit/pppd/cbcp.h @@ -0,0 +1,26 @@ +#ifndef CBCP_H +#define CBCP_H + +typedef struct cbcp_state { + int us_unit; /* Interface unit number */ + u_char us_id; /* Current id */ + u_char us_allowed; + int us_type; + char *us_number; /* Telefone Number */ +} cbcp_state; + +extern cbcp_state cbcp[]; + +extern struct protent cbcp_protent; + +#define CBCP_MINLEN 4 + +#define CBCP_REQ 1 +#define CBCP_RESP 2 +#define CBCP_ACK 3 + +#define CB_CONF_NO 1 +#define CB_CONF_USER 2 +#define CB_CONF_ADMIN 3 +#define CB_CONF_LIST 4 +#endif diff --git a/cpukit/pppd/ccp.c b/cpukit/pppd/ccp.c new file mode 100644 index 0000000000..1467c6e411 --- /dev/null +++ b/cpukit/pppd/ccp.c @@ -0,0 +1,1117 @@ +/* + * ccp.c - PPP Compression Control Protocol. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +#ifndef lint +/* static char rcsid[] = "$Id$"; */ +#endif + +#include +#include +#include +#include + +#include "pppd.h" +#include "fsm.h" +#include "ccp.h" +#include + +/* + * Protocol entry points from main code. + */ +static void ccp_init __P((int unit)); +static void ccp_open __P((int unit)); +static void ccp_close __P((int unit, char *)); +static void ccp_lowerup __P((int unit)); +static void ccp_lowerdown __P((int)); +static void ccp_input __P((int unit, u_char *pkt, int len)); +static void ccp_protrej __P((int unit)); +static int ccp_printpkt __P((u_char *pkt, int len, + void (*printer) __P((void *, char *, ...)), + void *arg)); +static void ccp_datainput __P((int unit, u_char *pkt, int len)); + +struct protent ccp_protent = { + PPP_CCP, + ccp_init, + ccp_input, + ccp_protrej, + ccp_lowerup, + ccp_lowerdown, + ccp_open, + ccp_close, + ccp_printpkt, + ccp_datainput, + 1, + "CCP", + NULL, + NULL, + NULL +}; + +fsm ccp_fsm[NUM_PPP]; +ccp_options ccp_wantoptions[NUM_PPP]; /* what to request the peer to use */ +ccp_options ccp_gotoptions[NUM_PPP]; /* what the peer agreed to do */ +ccp_options ccp_allowoptions[NUM_PPP]; /* what we'll agree to do */ +ccp_options ccp_hisoptions[NUM_PPP]; /* what we agreed to do */ + +/* + * Callbacks for fsm code. + */ +static void ccp_resetci __P((fsm *)); +static int ccp_cilen __P((fsm *)); +static void ccp_addci __P((fsm *, u_char *, int *)); +static int ccp_ackci __P((fsm *, u_char *, int)); +static int ccp_nakci __P((fsm *, u_char *, int)); +static int ccp_rejci __P((fsm *, u_char *, int)); +static int ccp_reqci __P((fsm *, u_char *, int *, int)); +static void ccp_up __P((fsm *)); +static void ccp_down __P((fsm *)); +static int ccp_extcode __P((fsm *, int, int, u_char *, int)); +static void ccp_rack_timeout __P((void *)); +static char *method_name __P((ccp_options *, ccp_options *)); + +static fsm_callbacks ccp_callbacks = { + ccp_resetci, + ccp_cilen, + ccp_addci, + ccp_ackci, + ccp_nakci, + ccp_rejci, + ccp_reqci, + ccp_up, + ccp_down, + NULL, + NULL, + NULL, + NULL, + ccp_extcode, + "CCP" +}; + +/* + * Do we want / did we get any compression? + */ +#define ANY_COMPRESS(opt) ((opt).deflate || (opt).bsd_compress \ + || (opt).predictor_1 || (opt).predictor_2) + +/* + * Local state (mainly for handling reset-reqs and reset-acks). + */ +static int ccp_localstate[NUM_PPP]; +#define RACK_PENDING 1 /* waiting for reset-ack */ +#define RREQ_REPEAT 2 /* send another reset-req if no reset-ack */ + +#define RACKTIMEOUT 1 /* second */ + +static int all_rejected[NUM_PPP]; /* we rejected all peer's options */ + +/* + * ccp_init - initialize CCP. + */ +static void +ccp_init(unit) + int unit; +{ + fsm *f = &ccp_fsm[unit]; + + f->unit = unit; + f->protocol = PPP_CCP; + f->callbacks = &ccp_callbacks; + fsm_init(f); + + memset(&ccp_wantoptions[unit], 0, sizeof(ccp_options)); + memset(&ccp_gotoptions[unit], 0, sizeof(ccp_options)); + memset(&ccp_allowoptions[unit], 0, sizeof(ccp_options)); + memset(&ccp_hisoptions[unit], 0, sizeof(ccp_options)); + + ccp_wantoptions[0].deflate = 1; + ccp_wantoptions[0].deflate_size = DEFLATE_MAX_SIZE; + ccp_wantoptions[0].deflate_correct = 1; + ccp_wantoptions[0].deflate_draft = 1; + ccp_allowoptions[0].deflate = 1; + ccp_allowoptions[0].deflate_size = DEFLATE_MAX_SIZE; + ccp_allowoptions[0].deflate_correct = 1; + ccp_allowoptions[0].deflate_draft = 1; + + ccp_wantoptions[0].bsd_compress = 1; + ccp_wantoptions[0].bsd_bits = BSD_MAX_BITS; + ccp_allowoptions[0].bsd_compress = 1; + ccp_allowoptions[0].bsd_bits = BSD_MAX_BITS; + + ccp_allowoptions[0].predictor_1 = 1; +} + +/* + * ccp_open - CCP is allowed to come up. + */ +static void +ccp_open(unit) + int unit; +{ + fsm *f = &ccp_fsm[unit]; + + if (f->state != OPENED) + ccp_flags_set(unit, 1, 0); + + /* + * Find out which compressors the kernel supports before + * deciding whether to open in silent mode. + */ + ccp_resetci(f); + if (!ANY_COMPRESS(ccp_gotoptions[unit])) + f->flags |= OPT_SILENT; + + fsm_open(f); +} + +/* + * ccp_close - Terminate CCP. + */ +static void +ccp_close(unit, reason) + int unit; + char *reason; +{ + ccp_flags_set(unit, 0, 0); + fsm_close(&ccp_fsm[unit], reason); +} + +/* + * ccp_lowerup - we may now transmit CCP packets. + */ +static void +ccp_lowerup(unit) + int unit; +{ + fsm_lowerup(&ccp_fsm[unit]); +} + +/* + * ccp_lowerdown - we may not transmit CCP packets. + */ +static void +ccp_lowerdown(unit) + int unit; +{ + fsm_lowerdown(&ccp_fsm[unit]); +} + +/* + * ccp_input - process a received CCP packet. + */ +static void +ccp_input(unit, p, len) + int unit; + u_char *p; + int len; +{ + fsm *f = &ccp_fsm[unit]; + int oldstate; + + /* + * Check for a terminate-request so we can print a message. + */ + oldstate = f->state; + fsm_input(f, p, len); + if (oldstate == OPENED && p[0] == TERMREQ && f->state != OPENED); + syslog(LOG_NOTICE, "Compression disabled by peer."); + + /* + * If we get a terminate-ack and we're not asking for compression, + * close CCP. + */ + if (oldstate == REQSENT && p[0] == TERMACK + && !ANY_COMPRESS(ccp_gotoptions[unit])) + ccp_close(unit, "No compression negotiated"); +} + +/* + * Handle a CCP-specific code. + */ +static int +ccp_extcode(f, code, id, p, len) + fsm *f; + int code, id; + u_char *p; + int len; +{ + switch (code) { + case CCP_RESETREQ: + if (f->state != OPENED) + break; + /* send a reset-ack, which the transmitter will see and + reset its compression state. */ + fsm_sdata(f, CCP_RESETACK, id, NULL, 0); + break; + + case CCP_RESETACK: + if (ccp_localstate[f->unit] & RACK_PENDING && id == f->reqid) { + ccp_localstate[f->unit] &= ~(RACK_PENDING | RREQ_REPEAT); + UNTIMEOUT(ccp_rack_timeout, f); + } + break; + + default: + return 0; + } + + return 1; +} + +/* + * ccp_protrej - peer doesn't talk CCP. + */ +static void +ccp_protrej(unit) + int unit; +{ + ccp_flags_set(unit, 0, 0); + fsm_lowerdown(&ccp_fsm[unit]); +} + +/* + * ccp_resetci - initialize at start of negotiation. + */ +static void +ccp_resetci(f) + fsm *f; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + u_char opt_buf[16]; + + *go = ccp_wantoptions[f->unit]; + all_rejected[f->unit] = 0; + + /* + * Check whether the kernel knows about the various + * compression methods we might request. + */ + if (go->bsd_compress) { + opt_buf[0] = CI_BSD_COMPRESS; + opt_buf[1] = CILEN_BSD_COMPRESS; + opt_buf[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, BSD_MIN_BITS); + if (ccp_test(f->unit, opt_buf, CILEN_BSD_COMPRESS, 0) <= 0) + go->bsd_compress = 0; + } + if (go->deflate) { + if (go->deflate_correct) { + opt_buf[0] = CI_DEFLATE; + opt_buf[1] = CILEN_DEFLATE; + opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_SIZE); + opt_buf[3] = DEFLATE_CHK_SEQUENCE; + if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0) + go->deflate_correct = 0; + } + if (go->deflate_draft) { + opt_buf[0] = CI_DEFLATE_DRAFT; + opt_buf[1] = CILEN_DEFLATE; + opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_SIZE); + opt_buf[3] = DEFLATE_CHK_SEQUENCE; + if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0) + go->deflate_draft = 0; + } + if (!go->deflate_correct && !go->deflate_draft) + go->deflate = 0; + } + if (go->predictor_1) { + opt_buf[0] = CI_PREDICTOR_1; + opt_buf[1] = CILEN_PREDICTOR_1; + if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_1, 0) <= 0) + go->predictor_1 = 0; + } + if (go->predictor_2) { + opt_buf[0] = CI_PREDICTOR_2; + opt_buf[1] = CILEN_PREDICTOR_2; + if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_2, 0) <= 0) + go->predictor_2 = 0; + } +} + +/* + * ccp_cilen - Return total length of our configuration info. + */ +static int +ccp_cilen(f) + fsm *f; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + + return (go->bsd_compress? CILEN_BSD_COMPRESS: 0) + + (go->deflate? CILEN_DEFLATE: 0) + + (go->predictor_1? CILEN_PREDICTOR_1: 0) + + (go->predictor_2? CILEN_PREDICTOR_2: 0); +} + +/* + * ccp_addci - put our requests in a packet. + */ +static void +ccp_addci(f, p, lenp) + fsm *f; + u_char *p; + int *lenp; +{ + int res; + ccp_options *go = &ccp_gotoptions[f->unit]; + u_char *p0 = p; + + /* + * Add the compression types that we can receive, in decreasing + * preference order. Get the kernel to allocate the first one + * in case it gets Acked. + */ + if (go->deflate) { + p[0] = go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT; + p[1] = CILEN_DEFLATE; + p[2] = DEFLATE_MAKE_OPT(go->deflate_size); + p[3] = DEFLATE_CHK_SEQUENCE; + for (;;) { + res = ccp_test(f->unit, p, CILEN_DEFLATE, 0); + if (res > 0) { + p += CILEN_DEFLATE; + break; + } + if (res < 0 || go->deflate_size <= DEFLATE_MIN_SIZE) { + go->deflate = 0; + break; + } + --go->deflate_size; + p[2] = DEFLATE_MAKE_OPT(go->deflate_size); + } + if (p != p0 && go->deflate_correct && go->deflate_draft) { + p[0] = CI_DEFLATE_DRAFT; + p[1] = CILEN_DEFLATE; + p[2] = p[2 - CILEN_DEFLATE]; + p[3] = DEFLATE_CHK_SEQUENCE; + p += CILEN_DEFLATE; + } + } + if (go->bsd_compress) { + p[0] = CI_BSD_COMPRESS; + p[1] = CILEN_BSD_COMPRESS; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits); + if (p != p0) { + p += CILEN_BSD_COMPRESS; /* not the first option */ + } else { + for (;;) { + res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0); + if (res > 0) { + p += CILEN_BSD_COMPRESS; + break; + } + if (res < 0 || go->bsd_bits <= BSD_MIN_BITS) { + go->bsd_compress = 0; + break; + } + --go->bsd_bits; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits); + } + } + } + /* XXX Should Predictor 2 be preferable to Predictor 1? */ + if (go->predictor_1) { + p[0] = CI_PREDICTOR_1; + p[1] = CILEN_PREDICTOR_1; + if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 0) <= 0) { + go->predictor_1 = 0; + } else { + p += CILEN_PREDICTOR_1; + } + } + if (go->predictor_2) { + p[0] = CI_PREDICTOR_2; + p[1] = CILEN_PREDICTOR_2; + if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 0) <= 0) { + go->predictor_2 = 0; + } else { + p += CILEN_PREDICTOR_2; + } + } + + go->method = (p > p0)? p0[0]: -1; + + *lenp = p - p0; +} + +/* + * ccp_ackci - process a received configure-ack, and return + * 1 iff the packet was OK. + */ +static int +ccp_ackci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + u_char *p0 = p; + + if (go->deflate) { + if (len < CILEN_DEFLATE + || p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT) + || p[1] != CILEN_DEFLATE + || p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + /* XXX Cope with first/fast ack */ + if (len == 0) + return 1; + if (go->deflate_correct && go->deflate_draft) { + if (len < CILEN_DEFLATE + || p[0] != CI_DEFLATE_DRAFT + || p[1] != CILEN_DEFLATE + || p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + } + } + if (go->bsd_compress) { + if (len < CILEN_BSD_COMPRESS + || p[0] != CI_BSD_COMPRESS || p[1] != CILEN_BSD_COMPRESS + || p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits)) + return 0; + p += CILEN_BSD_COMPRESS; + len -= CILEN_BSD_COMPRESS; + /* XXX Cope with first/fast ack */ + if (p == p0 && len == 0) + return 1; + } + if (go->predictor_1) { + if (len < CILEN_PREDICTOR_1 + || p[0] != CI_PREDICTOR_1 || p[1] != CILEN_PREDICTOR_1) + return 0; + p += CILEN_PREDICTOR_1; + len -= CILEN_PREDICTOR_1; + /* XXX Cope with first/fast ack */ + if (p == p0 && len == 0) + return 1; + } + if (go->predictor_2) { + if (len < CILEN_PREDICTOR_2 + || p[0] != CI_PREDICTOR_2 || p[1] != CILEN_PREDICTOR_2) + return 0; + p += CILEN_PREDICTOR_2; + len -= CILEN_PREDICTOR_2; + /* XXX Cope with first/fast ack */ + if (p == p0 && len == 0) + return 1; + } + + if (len != 0) + return 0; + return 1; +} + +/* + * ccp_nakci - process received configure-nak. + * Returns 1 iff the nak was OK. + */ +static int +ccp_nakci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + ccp_options no; /* options we've seen already */ + ccp_options try; /* options to ask for next time */ + + memset(&no, 0, sizeof(no)); + try = *go; + + if (go->deflate && len >= CILEN_DEFLATE + && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT) + && p[1] == CILEN_DEFLATE) { + no.deflate = 1; + /* + * Peer wants us to use a different code size or something. + * Stop asking for Deflate if we don't understand his suggestion. + */ + if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL + || DEFLATE_SIZE(p[2]) < DEFLATE_MIN_SIZE + || p[3] != DEFLATE_CHK_SEQUENCE) + try.deflate = 0; + else if (DEFLATE_SIZE(p[2]) < go->deflate_size) + try.deflate_size = DEFLATE_SIZE(p[2]); + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + if (go->deflate_correct && go->deflate_draft + && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT + && p[1] == CILEN_DEFLATE) { + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + } + } + + if (go->bsd_compress && len >= CILEN_BSD_COMPRESS + && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) { + no.bsd_compress = 1; + /* + * Peer wants us to use a different number of bits + * or a different version. + */ + if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION) + try.bsd_compress = 0; + else if (BSD_NBITS(p[2]) < go->bsd_bits) + try.bsd_bits = BSD_NBITS(p[2]); + p += CILEN_BSD_COMPRESS; + len -= CILEN_BSD_COMPRESS; + } + + /* + * Predictor-1 and 2 have no options, so they can't be Naked. + * + * XXX What should we do with any remaining options? + */ + + if (len != 0) + return 0; + + if (f->state != OPENED) + *go = try; + return 1; +} + +/* + * ccp_rejci - reject some of our suggested compression methods. + */ +static int +ccp_rejci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + ccp_options try; /* options to request next time */ + + try = *go; + + /* + * Cope with empty configure-rejects by ceasing to send + * configure-requests. + */ + if (len == 0 && all_rejected[f->unit]) + return -1; + + if (go->deflate && len >= CILEN_DEFLATE + && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT) + && p[1] == CILEN_DEFLATE) { + if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; /* Rej is bad */ + if (go->deflate_correct) + try.deflate_correct = 0; + else + try.deflate_draft = 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + if (go->deflate_correct && go->deflate_draft + && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT + && p[1] == CILEN_DEFLATE) { + if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; /* Rej is bad */ + try.deflate_draft = 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + } + if (!try.deflate_correct && !try.deflate_draft) + try.deflate = 0; + } + if (go->bsd_compress && len >= CILEN_BSD_COMPRESS + && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) { + if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits)) + return 0; + try.bsd_compress = 0; + p += CILEN_BSD_COMPRESS; + len -= CILEN_BSD_COMPRESS; + } + if (go->predictor_1 && len >= CILEN_PREDICTOR_1 + && p[0] == CI_PREDICTOR_1 && p[1] == CILEN_PREDICTOR_1) { + try.predictor_1 = 0; + p += CILEN_PREDICTOR_1; + len -= CILEN_PREDICTOR_1; + } + if (go->predictor_2 && len >= CILEN_PREDICTOR_2 + && p[0] == CI_PREDICTOR_2 && p[1] == CILEN_PREDICTOR_2) { + try.predictor_2 = 0; + p += CILEN_PREDICTOR_2; + len -= CILEN_PREDICTOR_2; + } + + if (len != 0) + return 0; + + if (f->state != OPENED) + *go = try; + + return 1; +} + +/* + * ccp_reqci - processed a received configure-request. + * Returns CONFACK, CONFNAK or CONFREJ and the packet modified + * appropriately. + */ +static int +ccp_reqci(f, p, lenp, dont_nak) + fsm *f; + u_char *p; + int *lenp; + int dont_nak; +{ + int ret, newret, res; + u_char *p0, *retp; + int len, clen, type, nb; + ccp_options *ho = &ccp_hisoptions[f->unit]; + ccp_options *ao = &ccp_allowoptions[f->unit]; + + ret = CONFACK; + retp = p0 = p; + len = *lenp; + + memset(ho, 0, sizeof(ccp_options)); + ho->method = (len > 0)? p[0]: -1; + + while (len > 0) { + newret = CONFACK; + if (len < 2 || p[1] < 2 || p[1] > len) { + /* length is bad */ + clen = len; + newret = CONFREJ; + + } else { + type = p[0]; + clen = p[1]; + + switch (type) { + case CI_DEFLATE: + case CI_DEFLATE_DRAFT: + if (!ao->deflate || clen != CILEN_DEFLATE + || (!ao->deflate_correct && type == CI_DEFLATE) + || (!ao->deflate_draft && type == CI_DEFLATE_DRAFT)) { + newret = CONFREJ; + break; + } + + ho->deflate = 1; + ho->deflate_size = nb = DEFLATE_SIZE(p[2]); + if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL + || p[3] != DEFLATE_CHK_SEQUENCE + || nb > ao->deflate_size || nb < DEFLATE_MIN_SIZE) { + newret = CONFNAK; + if (!dont_nak) { + p[2] = DEFLATE_MAKE_OPT(ao->deflate_size); + p[3] = DEFLATE_CHK_SEQUENCE; + /* fall through to test this #bits below */ + } else + break; + } + + /* + * Check whether we can do Deflate with the window + * size they want. If the window is too big, reduce + * it until the kernel can cope and nak with that. + * We only check this for the first option. + */ + if (p == p0) { + for (;;) { + res = ccp_test(f->unit, p, CILEN_DEFLATE, 1); + if (res > 0) + break; /* it's OK now */ + if (res < 0 || nb == DEFLATE_MIN_SIZE || dont_nak) { + newret = CONFREJ; + p[2] = DEFLATE_MAKE_OPT(ho->deflate_size); + break; + } + newret = CONFNAK; + --nb; + p[2] = DEFLATE_MAKE_OPT(nb); + } + } + break; + + case CI_BSD_COMPRESS: + if (!ao->bsd_compress || clen != CILEN_BSD_COMPRESS) { + newret = CONFREJ; + break; + } + + ho->bsd_compress = 1; + ho->bsd_bits = nb = BSD_NBITS(p[2]); + if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION + || nb > ao->bsd_bits || nb < BSD_MIN_BITS) { + newret = CONFNAK; + if (!dont_nak) { + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, ao->bsd_bits); + /* fall through to test this #bits below */ + } else + break; + } + + /* + * Check whether we can do BSD-Compress with the code + * size they want. If the code size is too big, reduce + * it until the kernel can cope and nak with that. + * We only check this for the first option. + */ + if (p == p0) { + for (;;) { + res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 1); + if (res > 0) + break; + if (res < 0 || nb == BSD_MIN_BITS || dont_nak) { + newret = CONFREJ; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, + ho->bsd_bits); + break; + } + newret = CONFNAK; + --nb; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb); + } + } + break; + + case CI_PREDICTOR_1: + if (!ao->predictor_1 || clen != CILEN_PREDICTOR_1) { + newret = CONFREJ; + break; + } + + ho->predictor_1 = 1; + if (p == p0 + && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 1) <= 0) { + newret = CONFREJ; + } + break; + + case CI_PREDICTOR_2: + if (!ao->predictor_2 || clen != CILEN_PREDICTOR_2) { + newret = CONFREJ; + break; + } + + ho->predictor_2 = 1; + if (p == p0 + && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 1) <= 0) { + newret = CONFREJ; + } + break; + + default: + newret = CONFREJ; + } + } + + if (newret == CONFNAK && dont_nak) + newret = CONFREJ; + if (!(newret == CONFACK || (newret == CONFNAK && ret == CONFREJ))) { + /* we're returning this option */ + if (newret == CONFREJ && ret == CONFNAK) + retp = p0; + ret = newret; + if (p != retp) + BCOPY(p, retp, clen); + retp += clen; + } + + p += clen; + len -= clen; + } + + if (ret != CONFACK) { + if (ret == CONFREJ && *lenp == retp - p0) + all_rejected[f->unit] = 1; + else + *lenp = retp - p0; + } + return ret; +} + +/* + * Make a string name for a compression method (or 2). + */ +static char * +method_name(opt, opt2) + ccp_options *opt, *opt2; +{ + static char result[64]; + + if (!ANY_COMPRESS(*opt)) + return "(none)"; + switch (opt->method) { + case CI_DEFLATE: + case CI_DEFLATE_DRAFT: + if (opt2 != NULL && opt2->deflate_size != opt->deflate_size) + sprintf(result, "Deflate%s (%d/%d)", + (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""), + opt->deflate_size, opt2->deflate_size); + else + sprintf(result, "Deflate%s (%d)", + (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""), + opt->deflate_size); + break; + case CI_BSD_COMPRESS: + if (opt2 != NULL && opt2->bsd_bits != opt->bsd_bits) + sprintf(result, "BSD-Compress (%d/%d)", opt->bsd_bits, + opt2->bsd_bits); + else + sprintf(result, "BSD-Compress (%d)", opt->bsd_bits); + break; + case CI_PREDICTOR_1: + return "Predictor 1"; + case CI_PREDICTOR_2: + return "Predictor 2"; + default: + sprintf(result, "Method %d", opt->method); + } + return result; +} + +/* + * CCP has come up - inform the kernel driver and log a message. + */ +static void +ccp_up(f) + fsm *f; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + ccp_options *ho = &ccp_hisoptions[f->unit]; + char method1[64]; + + ccp_flags_set(f->unit, 1, 1); + if (ANY_COMPRESS(*go)) { + if (ANY_COMPRESS(*ho)) { + if (go->method == ho->method) { + syslog(LOG_NOTICE, "%s compression enabled", + method_name(go, ho)) +; + } else { + strcpy(method1, method_name(go, NULL)); + syslog(LOG_NOTICE, "%s / %s compression enabled", + method1, method_name(ho, NULL)) +; + } + } else + syslog(LOG_NOTICE, "%s receive compression enabled", + method_name(go, NULL)) +; + } else if (ANY_COMPRESS(*ho)) + syslog(LOG_NOTICE, "%s transmit compression enabled", + method_name(ho, NULL)) +; +} + +/* + * CCP has gone down - inform the kernel driver. + */ +static void +ccp_down(f) + fsm *f; +{ + if (ccp_localstate[f->unit] & RACK_PENDING) + UNTIMEOUT(ccp_rack_timeout, f); + ccp_localstate[f->unit] = 0; + ccp_flags_set(f->unit, 1, 0); +} + +/* + * Print the contents of a CCP packet. + */ +static char *ccp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", + NULL, NULL, NULL, NULL, NULL, NULL, + "ResetReq", "ResetAck", +}; + +static int +ccp_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + u_char *p0, *optend; + int code, id, len; + int optlen; + + p0 = p; + if (plen < HEADERLEN) + return 0; + code = p[0]; + id = p[1]; + len = (p[2] << 8) + p[3]; + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(ccp_codenames) / sizeof(char *) + && ccp_codenames[code-1] != NULL) + printer(arg, " %s", ccp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + p += HEADERLEN; + + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print list of possible compression methods */ + while (len >= 2) { + code = p[0]; + optlen = p[1]; + if (optlen < 2 || optlen > len) + break; + printer(arg, " <"); + len -= optlen; + optend = p + optlen; + switch (code) { + case CI_DEFLATE: + case CI_DEFLATE_DRAFT: + if (optlen >= CILEN_DEFLATE) { + printer(arg, "deflate%s %d", + (code == CI_DEFLATE_DRAFT? "(old#)": ""), + DEFLATE_SIZE(p[2])); + if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL) + printer(arg, " method %d", DEFLATE_METHOD(p[2])); + if (p[3] != DEFLATE_CHK_SEQUENCE) + printer(arg, " check %d", p[3]); + p += CILEN_DEFLATE; + } + break; + case CI_BSD_COMPRESS: + if (optlen >= CILEN_BSD_COMPRESS) { + printer(arg, "bsd v%d %d", BSD_VERSION(p[2]), + BSD_NBITS(p[2])); + p += CILEN_BSD_COMPRESS; + } + break; + case CI_PREDICTOR_1: + if (optlen >= CILEN_PREDICTOR_1) { + printer(arg, "predictor 1"); + p += CILEN_PREDICTOR_1; + } + break; + case CI_PREDICTOR_2: + if (optlen >= CILEN_PREDICTOR_2) { + printer(arg, "predictor 2"); + p += CILEN_PREDICTOR_2; + } + break; + } +/* while (p < optend) + printer(arg, " %.2x", *p++); + printer(arg, ">"); +*/ } + break; + + case TERMACK: + case TERMREQ: +/* if (len > 0 && *p >= ' ' && *p < 0x7f) { + print_string(p, len, printer, arg); + p += len; + len = 0; + } +*/ break; + } + + /* dump out the rest of the packet in hex */ +/* while (--len >= 0) + printer(arg, " %.2x", *p++); +*/ + return p - p0; +} + +/* + * We have received a packet that the decompressor failed to + * decompress. Here we would expect to issue a reset-request, but + * Motorola has a patent on resetting the compressor as a result of + * detecting an error in the decompressed data after decompression. + * (See US patent 5,130,993; international patent publication number + * WO 91/10289; Australian patent 73296/91.) + * + * So we ask the kernel whether the error was detected after + * decompression; if it was, we take CCP down, thus disabling + * compression :-(, otherwise we issue the reset-request. + */ +static void +ccp_datainput(unit, pkt, len) + int unit; + u_char *pkt; + int len; +{ + fsm *f; + + f = &ccp_fsm[unit]; + if (f->state == OPENED) { + if (ccp_fatal_error(unit)) { + /* + * Disable compression by taking CCP down. + */ + syslog(LOG_ERR, "Lost compression sync: disabling compression"); + ccp_close(unit, "Lost compression sync"); + } else { + /* + * Send a reset-request to reset the peer's compressor. + * We don't do that if we are still waiting for an + * acknowledgement to a previous reset-request. + */ + if (!(ccp_localstate[f->unit] & RACK_PENDING)) { + fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0); + TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT); + ccp_localstate[f->unit] |= RACK_PENDING; + } else + ccp_localstate[f->unit] |= RREQ_REPEAT; + } + } +} + +/* + * Timeout waiting for reset-ack. + */ +static void +ccp_rack_timeout(arg) + void *arg; +{ + fsm *f = arg; + + if (f->state == OPENED && ccp_localstate[f->unit] & RREQ_REPEAT) { + fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0); + TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT); + ccp_localstate[f->unit] &= ~RREQ_REPEAT; + } else + ccp_localstate[f->unit] &= ~RACK_PENDING; +} + diff --git a/cpukit/pppd/ccp.h b/cpukit/pppd/ccp.h new file mode 100644 index 0000000000..af421d48c3 --- /dev/null +++ b/cpukit/pppd/ccp.h @@ -0,0 +1,48 @@ +/* + * ccp.h - Definitions for PPP Compression Control Protocol. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + * + * $Id$ + */ + +typedef struct ccp_options { + u_int bsd_compress: 1; /* do BSD Compress? */ + u_int deflate: 1; /* do Deflate? */ + u_int predictor_1: 1; /* do Predictor-1? */ + u_int predictor_2: 1; /* do Predictor-2? */ + u_int deflate_correct: 1; /* use correct code for deflate? */ + u_int deflate_draft: 1; /* use draft RFC code for deflate? */ + u_short bsd_bits; /* # bits/code for BSD Compress */ + u_short deflate_size; /* lg(window size) for Deflate */ + short method; /* code for chosen compression method */ +} ccp_options; + +extern fsm ccp_fsm[]; +extern ccp_options ccp_wantoptions[]; +extern ccp_options ccp_gotoptions[]; +extern ccp_options ccp_allowoptions[]; +extern ccp_options ccp_hisoptions[]; + +extern struct protent ccp_protent; diff --git a/cpukit/pppd/chap.c b/cpukit/pppd/chap.c new file mode 100644 index 0000000000..2a349c9b60 --- /dev/null +++ b/cpukit/pppd/chap.c @@ -0,0 +1,872 @@ +/* + * chap.c - Challenge Handshake Authentication Protocol. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Gregory M. Christy. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +/* static char rcsid[] = "$Id$"; */ +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include +#include + +#include "pppd.h" +#include "chap.h" +#include "md5.h" +#ifdef CHAPMS +#include "chap_ms.h" +#endif + +/* + * Protocol entry points. + */ +static void ChapInit __P((int)); +static void ChapLowerUp __P((int)); +static void ChapLowerDown __P((int)); +static void ChapInput __P((int, u_char *, int)); +static void ChapProtocolReject __P((int)); +static int ChapPrintPkt __P((u_char *, int, + void (*) __P((void *, char *, ...)), void *)); + +struct protent chap_protent = { + PPP_CHAP, + ChapInit, + ChapInput, + ChapProtocolReject, + ChapLowerUp, + ChapLowerDown, + NULL, + NULL, + ChapPrintPkt, + NULL, + 1, + "CHAP", + NULL, + NULL, + NULL +}; + +chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */ + +static void ChapChallengeTimeout __P((void *)); +static void ChapResponseTimeout __P((void *)); +static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int)); +static void ChapRechallenge __P((void *)); +static void ChapReceiveResponse __P((chap_state *, u_char *, int, int)); +static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int)); +static void ChapReceiveFailure __P((chap_state *, u_char *, int, int)); +static void ChapSendStatus __P((chap_state *, int)); +static void ChapSendChallenge __P((chap_state *)); +static void ChapSendResponse __P((chap_state *)); +static void ChapGenChallenge __P((chap_state *)); +/* #include */ + +extern double drand48 __P((void)); +/*{ + return (((double)rand())/RAND_MAX); +} +*/ +extern void srand48 __P((long)); + +/* + * ChapInit - Initialize a CHAP unit. + */ +static void +ChapInit(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + BZERO(cstate, sizeof(*cstate)); + cstate->unit = unit; + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; + cstate->timeouttime = CHAP_DEFTIMEOUT; + cstate->max_transmits = CHAP_DEFTRANSMITS; + /* random number generator is initialized in magic_init */ +} + + +/* + * ChapAuthWithPeer - Authenticate us with our peer (start client). + * + */ +void +ChapAuthWithPeer(unit, our_name, digest) + int unit; + char *our_name; + int digest; +{ + chap_state *cstate = &chap[unit]; + + cstate->resp_name = our_name; + cstate->resp_type = digest; + + if (cstate->clientstate == CHAPCS_INITIAL || + cstate->clientstate == CHAPCS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->clientstate = CHAPCS_PENDING; + return; + } + + /* + * We get here as a result of LCP coming up. + * So even if CHAP was open before, we will + * have to re-authenticate ourselves. + */ + cstate->clientstate = CHAPCS_LISTEN; +} + + +/* + * ChapAuthPeer - Authenticate our peer (start server). + */ +void +ChapAuthPeer(unit, our_name, digest) + int unit; + char *our_name; + int digest; +{ + chap_state *cstate = &chap[unit]; + + cstate->chal_name = our_name; + cstate->chal_type = digest; + + if (cstate->serverstate == CHAPSS_INITIAL || + cstate->serverstate == CHAPSS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->serverstate = CHAPSS_PENDING; + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); /* crank it up dude! */ + cstate->serverstate = CHAPSS_INITIAL_CHAL; +} + + +/* + * ChapChallengeTimeout - Timeout expired on sending challenge. + */ +static void +ChapChallengeTimeout(arg) + void *arg; +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending challenges, don't worry. then again we */ + /* probably shouldn't be here either */ + if (cstate->serverstate != CHAPSS_INITIAL_CHAL && + cstate->serverstate != CHAPSS_RECHALLENGE) + return; + + if (cstate->chal_transmits >= cstate->max_transmits) { + /* give up on peer */ + syslog(LOG_ERR, "Peer failed to respond to CHAP challenge"); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + return; + } + + ChapSendChallenge(cstate); /* Re-send challenge */ +} + + +/* + * ChapResponseTimeout - Timeout expired on sending response. + */ +static void +ChapResponseTimeout(arg) + void *arg; +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->clientstate != CHAPCS_RESPONSE) + return; + + ChapSendResponse(cstate); /* re-send response */ +} + + +/* + * ChapRechallenge - Time to challenge the peer again. + */ +static void +ChapRechallenge(arg) + void *arg; +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->serverstate != CHAPSS_OPEN) + return; + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_RECHALLENGE; +} + + +/* + * ChapLowerUp - The lower layer is up. + * + * Start up if we have pending requests. + */ +static void +ChapLowerUp(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + if (cstate->clientstate == CHAPCS_INITIAL) + cstate->clientstate = CHAPCS_CLOSED; + else if (cstate->clientstate == CHAPCS_PENDING) + cstate->clientstate = CHAPCS_LISTEN; + + if (cstate->serverstate == CHAPSS_INITIAL) + cstate->serverstate = CHAPSS_CLOSED; + else if (cstate->serverstate == CHAPSS_PENDING) { + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_INITIAL_CHAL; + } +} + + +/* + * ChapLowerDown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +ChapLowerDown(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + /* Timeout(s) pending? Cancel if so. */ + if (cstate->serverstate == CHAPSS_INITIAL_CHAL || + cstate->serverstate == CHAPSS_RECHALLENGE) + UNTIMEOUT(ChapChallengeTimeout, cstate); + else if (cstate->serverstate == CHAPSS_OPEN + && cstate->chal_interval != 0) + UNTIMEOUT(ChapRechallenge, cstate); + if (cstate->clientstate == CHAPCS_RESPONSE) + UNTIMEOUT(ChapResponseTimeout, cstate); + + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; +} + + +/* + * ChapProtocolReject - Peer doesn't grok CHAP. + */ +static void +ChapProtocolReject(unit) + int unit; +{ + chap_state *cstate = &chap[unit]; + + if (cstate->serverstate != CHAPSS_INITIAL && + cstate->serverstate != CHAPSS_CLOSED) + auth_peer_fail(unit, PPP_CHAP); + if (cstate->clientstate != CHAPCS_INITIAL && + cstate->clientstate != CHAPCS_CLOSED) + auth_withpeer_fail(unit, PPP_CHAP); + ChapLowerDown(unit); /* shutdown chap */ +} + + +/* + * ChapInput - Input CHAP packet. + */ +static void +ChapInput(unit, inpacket, packet_len) + int unit; + u_char *inpacket; + int packet_len; +{ + chap_state *cstate = &chap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (packet_len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header.")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length.")); + return; + } + if (len > packet_len) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet.")); + return; + } + len -= CHAP_HEADERLEN; + + /* + * Action depends on code (as in fact it usually does :-). + */ + switch (code) { + case CHAP_CHALLENGE: + ChapReceiveChallenge(cstate, inp, id, len); + break; + + case CHAP_RESPONSE: + ChapReceiveResponse(cstate, inp, id, len); + break; + + case CHAP_FAILURE: + ChapReceiveFailure(cstate, inp, id, len); + break; + + case CHAP_SUCCESS: + ChapReceiveSuccess(cstate, inp, id, len); + break; + + default: /* Need code reject? */ + syslog(LOG_WARNING, "Unknown CHAP code (%d) received.", code); + break; + } +} + + +/* + * ChapReceiveChallenge - Receive Challenge and send Response. + */ +static void +ChapReceiveChallenge(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + int id; + int len; +{ + int rchallenge_len; + u_char *rchallenge; + int secret_len; + char secret[MAXSECRETLEN]; + char rhostname[256]; + MD5_CTX mdContext; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.", id)); + if (cstate->clientstate == CHAPCS_CLOSED || + cstate->clientstate == CHAPCS_PENDING) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d", + cstate->clientstate)); + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.")); + return; + } + + GETCHAR(rchallenge_len, inp); + len -= sizeof (u_char) + rchallenge_len; /* now name field length */ + if (len < 0) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.")); + return; + } + rchallenge = inp; + INCPTR(rchallenge_len, inp); + + if (len >= sizeof(rhostname)) + len = sizeof(rhostname) - 1; + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field '%s'", + rhostname)); + + /* Microsoft doesn't send their name back in the PPP packet */ + if (remote_name[0] != 0 && (explicit_remote || rhostname[0] == 0)) { + strncpy(rhostname, remote_name, sizeof(rhostname)); + rhostname[sizeof(rhostname) - 1] = 0; + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: using '%s' as remote name", + rhostname)); + } + + /* get secret for authenticating ourselves with the specified host */ + if (!get_secret(cstate->unit, cstate->resp_name, rhostname, + secret, &secret_len, 0)) { + secret_len = 0; /* assume null secret if can't find one */ + syslog(LOG_WARNING, "No CHAP secret found for authenticating us to %s", + rhostname); + } + + /* cancel response send timeout if necessary */ + if (cstate->clientstate == CHAPCS_RESPONSE) + UNTIMEOUT(ChapResponseTimeout, cstate); + + cstate->resp_id = id; + cstate->resp_transmits = 0; + + /* generate MD based on negotiated type */ + switch (cstate->resp_type) { + + case CHAP_DIGEST_MD5: + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->resp_id, 1); + MD5Update(&mdContext, secret, secret_len); + MD5Update(&mdContext, rchallenge, rchallenge_len); + MD5Final(hash, &mdContext); + BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE); + cstate->resp_length = MD5_SIGNATURE_SIZE; + break; + +#ifdef CHAPMS + case CHAP_MICROSOFT: + ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len); + break; +#endif + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->resp_type)); + return; + } + + BZERO(secret, sizeof(secret)); + ChapSendResponse(cstate); +} + + +/* + * ChapReceiveResponse - Receive and process response. + */ +static void +ChapReceiveResponse(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + int id; + int len; +{ + u_char *remmd, remmd_len; + int secret_len, old_state; + int code; + char rhostname[256]; + MD5_CTX mdContext; + char secret[MAXSECRETLEN]; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.", id)); + + if (cstate->serverstate == CHAPSS_CLOSED || + cstate->serverstate == CHAPSS_PENDING) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d", + cstate->serverstate)); + return; + } + + if (id != cstate->chal_id) + return; /* doesn't match ID of last challenge */ + + /* + * If we have received a duplicate or bogus Response, + * we have to send the same answer (Success/Failure) + * as we did for the first Response we saw. + */ + if (cstate->serverstate == CHAPSS_OPEN) { + ChapSendStatus(cstate, CHAP_SUCCESS); + return; + } + if (cstate->serverstate == CHAPSS_BADAUTH) { + ChapSendStatus(cstate, CHAP_FAILURE); + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.")); + return; + } + GETCHAR(remmd_len, inp); /* get length of MD */ + remmd = inp; /* get pointer to MD */ + INCPTR(remmd_len, inp); + + len -= sizeof (u_char) + remmd_len; + if (len < 0) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.")); + return; + } + + UNTIMEOUT(ChapChallengeTimeout, cstate); + + if (len >= sizeof(rhostname)) + len = sizeof(rhostname) - 1; + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s", + rhostname)); + + /* + * Get secret for authenticating them with us, + * do the hash ourselves, and compare the result. + */ + code = CHAP_FAILURE; + if (!get_secret(cstate->unit, rhostname, cstate->chal_name, + secret, &secret_len, 1)) { + syslog(LOG_WARNING, "No CHAP secret found for authenticating %s", + rhostname); + } else { + + /* generate MD based on negotiated type */ + switch (cstate->chal_type) { + + case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + if (remmd_len != MD5_SIGNATURE_SIZE) + break; /* it's not even the right length */ + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->chal_id, 1); + MD5Update(&mdContext, secret, secret_len); + MD5Update(&mdContext, cstate->challenge, cstate->chal_len); + MD5Final(hash, &mdContext); + + /* compare local and remote MDs and send the appropriate status */ + if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) + code = CHAP_SUCCESS; /* they are the same! */ + break; + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->chal_type)); + } + } + + BZERO(secret, sizeof(secret)); + ChapSendStatus(cstate, code); + + if (code == CHAP_SUCCESS) { + old_state = cstate->serverstate; + cstate->serverstate = CHAPSS_OPEN; + if (old_state == CHAPSS_INITIAL_CHAL) { + auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len); + } + if (cstate->chal_interval != 0) + TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval); + syslog(LOG_NOTICE, "CHAP peer authentication succeeded for %s", + rhostname); + + } else { + syslog(LOG_ERR, "CHAP peer authentication failed for remote host %s", + rhostname); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + } +} + +/* + * ChapReceiveSuccess - Receive Success + */ +static void +ChapReceiveSuccess(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + u_char id; + int len; +{ + + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id)); + + if (cstate->clientstate == CHAPCS_OPEN) + /* presumably an answer to a duplicate response */ + return; + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n", + cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) + PRINTMSG(inp, len); + + cstate->clientstate = CHAPCS_OPEN; + + auth_withpeer_success(cstate->unit, PPP_CHAP); +} + + +/* + * ChapReceiveFailure - Receive failure. + */ +static void +ChapReceiveFailure(cstate, inp, id, len) + chap_state *cstate; + u_char *inp; + u_char id; + int len; +{ + CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id)); + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n", + cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) + PRINTMSG(inp, len); + + syslog(LOG_ERR, "CHAP authentication failed"); + auth_withpeer_fail(cstate->unit, PPP_CHAP); +} + + +/* + * ChapSendChallenge - Send an Authenticate challenge. + */ +static void +ChapSendChallenge(cstate) + chap_state *cstate; +{ + u_char *outp; + int chal_len, name_len; + int outlen; + + chal_len = cstate->chal_len; + name_len = strlen(cstate->chal_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len; + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */ + + PUTCHAR(CHAP_CHALLENGE, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + + PUTCHAR(chal_len, outp); /* put length of challenge */ + BCOPY(cstate->challenge, outp, chal_len); + INCPTR(chal_len, outp); + + BCOPY(cstate->chal_name, outp, name_len); /* append hostname */ + + output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN); + + CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->chal_id)); + + TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime); + ++cstate->chal_transmits; +} + + +/* + * ChapSendStatus - Send a status response (ack or nak). + */ +static void +ChapSendStatus(cstate, code) + chap_state *cstate; + int code; +{ + u_char *outp; + int outlen, msglen; + char msg[256]; + + if (code == CHAP_SUCCESS) + sprintf(msg, "Welcome to %s.", hostname); + else + sprintf(msg, "I don't like you. Go 'way."); + msglen = strlen(msg); + + outlen = CHAP_HEADERLEN + msglen; + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a header */ + + PUTCHAR(code, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + BCOPY(msg, outp, msglen); + output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN); + + CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code, + cstate->chal_id)); +} + +/* + * ChapGenChallenge is used to generate a pseudo-random challenge string of + * a pseudo-random length between min_len and max_len. The challenge + * string and its length are stored in *cstate, and various other fields of + * *cstate are initialized. + */ + +static void +ChapGenChallenge(cstate) + chap_state *cstate; +{ + int chal_len; + u_char *ptr = cstate->challenge; + unsigned int i; + + /* pick a random challenge length between MIN_CHALLENGE_LENGTH and + MAX_CHALLENGE_LENGTH */ + chal_len = (unsigned) ((drand48() * + (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) + + MIN_CHALLENGE_LENGTH); + cstate->chal_len = chal_len; + cstate->chal_id = ++cstate->id; + cstate->chal_transmits = 0; + + /* generate a random string */ + for (i = 0; i < chal_len; i++ ) + *ptr++ = (char) (drand48() * 0xff); +} + +/* + * ChapSendResponse - send a response packet with values as specified + * in *cstate. + */ +/* ARGSUSED */ +static void +ChapSendResponse(cstate) + chap_state *cstate; +{ + u_char *outp; + int outlen, md_len, name_len; + + md_len = cstate->resp_length; + name_len = strlen(cstate->resp_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len; + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_CHAP); + + PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */ + PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */ + PUTSHORT(outlen, outp); /* packet length */ + + PUTCHAR(md_len, outp); /* length of MD */ + BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */ + INCPTR(md_len, outp); + + BCOPY(cstate->resp_name, outp, name_len); /* append our name */ + + /* send the packet */ + output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN); + + cstate->clientstate = CHAPCS_RESPONSE; + TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime); + ++cstate->resp_transmits; +} + +/* + * ChapPrintPkt - print the contents of a CHAP packet. + */ +static char *ChapCodenames[] = { + "Challenge", "Response", "Success", "Failure" +}; + +static int +ChapPrintPkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int code, id, len; + int clen, nlen; + u_char x; + if (plen < CHAP_HEADERLEN) + return 0; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < CHAP_HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) + printer(arg, " %s", ChapCodenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= CHAP_HEADERLEN; + switch (code) { + case CHAP_CHALLENGE: + case CHAP_RESPONSE: + if (len < 1) + break; + clen = p[0]; + if (len < clen + 1) + break; + ++p; + nlen = len - clen - 1; + printer(arg, " <"); + for (; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, "%.2x", x); + } + printer(arg, ">, name = "); + print_string((char *)p, nlen, printer, arg); + break; + case CHAP_FAILURE: + case CHAP_SUCCESS: + printer(arg, " "); + print_string((char *)p, len, printer, arg); + break; + default: + for (clen = len; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } + } + return len + CHAP_HEADERLEN; +} diff --git a/cpukit/pppd/chap.h b/cpukit/pppd/chap.h new file mode 100644 index 0000000000..6e2cc45e89 --- /dev/null +++ b/cpukit/pppd/chap.h @@ -0,0 +1,124 @@ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the author. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +#ifndef __CHAP_INCLUDE__ + +/* Code + ID + length */ +#define CHAP_HEADERLEN 4 + +/* + * CHAP codes. + */ + +#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ +#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ +#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */ +#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ + +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 + +/* + * Challenge lengths (for challenges we send) and other limits. + */ +#define MIN_CHALLENGE_LENGTH 32 +#define MAX_CHALLENGE_LENGTH 64 +#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */ + +/* + * Each interface is described by a chap structure. + */ + +typedef struct chap_state { + int unit; /* Interface unit number */ + int clientstate; /* Client state */ + int serverstate; /* Server state */ + u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */ + u_char chal_len; /* challenge length */ + u_char chal_id; /* ID of last challenge */ + u_char chal_type; /* hash algorithm for challenges */ + u_char id; /* Current id */ + char *chal_name; /* Our name to use with challenge */ + int chal_interval; /* Time until we challenge peer again */ + int timeouttime; /* Timeout time in seconds */ + int max_transmits; /* Maximum # of challenge transmissions */ + int chal_transmits; /* Number of transmissions of challenge */ + int resp_transmits; /* Number of transmissions of response */ + u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */ + u_char resp_length; /* length of response */ + u_char resp_id; /* ID for response messages */ + u_char resp_type; /* hash algorithm for responses */ + char *resp_name; /* Our name to send with response */ +} chap_state; + + +/* + * Client (peer) states. + */ +#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */ +#define CHAPCS_LISTEN 3 /* Listening for a challenge */ +#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */ +#define CHAPCS_OPEN 5 /* We've received Success */ + +/* + * Server (authenticator) states. + */ +#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPSS_PENDING 2 /* Auth peer when lower up */ +#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */ +#define CHAPSS_OPEN 4 /* We've sent a Success msg */ +#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */ +#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */ + +/* + * Timeouts. + */ +#define CHAP_DEFTIMEOUT 3 /* Timeout time in seconds */ +#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */ + +extern chap_state chap[]; + +void ChapAuthWithPeer __P((int, char *, int)); +void ChapAuthPeer __P((int, char *, int)); + +extern struct protent chap_protent; + +#define __CHAP_INCLUDE__ +#endif /* __CHAP_INCLUDE__ */ diff --git a/cpukit/pppd/chap_ms.c b/cpukit/pppd/chap_ms.c new file mode 100644 index 0000000000..2de98dbb66 --- /dev/null +++ b/cpukit/pppd/chap_ms.c @@ -0,0 +1,335 @@ +/* + * chap_ms.c - Microsoft MS-CHAP compatible implementation. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997 + * + * Implemented LANManager type password response to MS-CHAP challenges. + * Now pppd provides both NT style and LANMan style blocks, and the + * prefered is set by option "ms-lanman". Default is to use NT. + * The hash text (StdText) was taken from Win95 RASAPI32.DLL. + * + * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 + */ + +#ifndef lint +/* static char rcsid[] = "$Id$"; */ +#endif + +#ifdef CHAPMS + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_CRYPT_H +#include +#endif + +#include "pppd.h" +#include "chap.h" +#include "chap_ms.h" +#include "md4.h" + +#ifndef USE_CRYPT +#include +#endif + +typedef struct { + u_char LANManResp[24]; + u_char NTResp[24]; + u_char UseNT; /* If 1, ignore the LANMan response field */ +} MS_ChapResponse; +/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), + in case this struct gets padded. */ + + +static void ChallengeResponse __P((u_char *, u_char *, u_char *)); +static void DesEncrypt __P((u_char *, u_char *, u_char *)); +static void MakeKey __P((u_char *, u_char *)); +static u_char Get7Bits __P((u_char *, int)); +static void ChapMS_NT __P((char *, int, char *, int, MS_ChapResponse *)); +#ifdef MSLANMAN +static void ChapMS_LANMan __P((char *, int, char *, int, MS_ChapResponse *)); +#endif + +#ifdef USE_CRYPT +static void Expand __P((u_char *, u_char *)); +static void Collapse __P((u_char *, u_char *)); +#endif + +static void +ChallengeResponse(challenge, pwHash, response) + u_char *challenge; /* IN 8 octets */ + u_char *pwHash; /* IN 16 octets */ + u_char *response; /* OUT 24 octets */ +{ + char ZPasswordHash[21]; + + BZERO(ZPasswordHash, sizeof(ZPasswordHash)); + BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE); + +#if 0 + log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG); +#endif + + DesEncrypt(challenge, ZPasswordHash + 0, response + 0); + DesEncrypt(challenge, ZPasswordHash + 7, response + 8); + DesEncrypt(challenge, ZPasswordHash + 14, response + 16); + +#if 0 + log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG); +#endif +} + + +#ifdef USE_CRYPT +static void +DesEncrypt(clear, key, cipher) + u_char *clear; /* IN 8 octets */ + u_char *key; /* IN 7 octets */ + u_char *cipher; /* OUT 8 octets */ +{ + u_char des_key[8]; + u_char crypt_key[66]; + u_char des_input[66]; + + MakeKey(key, des_key); + + Expand(des_key, crypt_key); + setkey(crypt_key); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + Expand(clear, des_input); + encrypt(des_input, 0); + Collapse(des_input, cipher); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#else /* USE_CRYPT */ + +static void +DesEncrypt(clear, key, cipher) + u_char *clear; /* IN 8 octets */ + u_char *key; /* IN 7 octets */ + u_char *cipher; /* OUT 8 octets */ +{ + des_cblock des_key; + des_key_schedule key_schedule; + + MakeKey(key, des_key); + + des_set_key(&des_key, key_schedule); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#endif /* USE_CRYPT */ + + +static u_char Get7Bits(input, startBit) + u_char *input; + int startBit; +{ + register unsigned int word; + + word = (unsigned)input[startBit / 8] << 8; + word |= (unsigned)input[startBit / 8 + 1]; + + word >>= 15 - (startBit % 8 + 7); + + return word & 0xFE; +} + +#ifdef USE_CRYPT + +/* in == 8-byte string (expanded version of the 56-bit key) + * out == 64-byte string where each byte is either 1 or 0 + * Note that the low-order "bit" is always ignored by by setkey() + */ +static void Expand(in, out) + u_char *in; + u_char *out; +{ + int j, c; + int i; + + for(i = 0; i < 64; in++){ + c = *in; + for(j = 7; j >= 0; j--) + *out++ = (c >> j) & 01; + i += 8; + } +} + +/* The inverse of Expand + */ +static void Collapse(in, out) + u_char *in; + u_char *out; +{ + int j; + int i; + unsigned int c; + + for (i = 0; i < 64; i += 8, out++) { + c = 0; + for (j = 7; j >= 0; j--, in++) + c |= *in << j; + *out = c & 0xff; + } +} +#endif + +static void MakeKey(key, des_key) + u_char *key; /* IN 56 bit DES key missing parity bits */ + u_char *des_key; /* OUT 64 bit DES key with parity bits added */ +{ + des_key[0] = Get7Bits(key, 0); + des_key[1] = Get7Bits(key, 7); + des_key[2] = Get7Bits(key, 14); + des_key[3] = Get7Bits(key, 21); + des_key[4] = Get7Bits(key, 28); + des_key[5] = Get7Bits(key, 35); + des_key[6] = Get7Bits(key, 42); + des_key[7] = Get7Bits(key, 49); + +#ifndef USE_CRYPT + des_set_odd_parity((des_cblock *)des_key); +#endif + +#if 0 + CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X", + key[0], key[1], key[2], key[3], key[4], key[5], key[6])); + CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X", + des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7])); +#endif +} + +static void +ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, response) + char *rchallenge; + int rchallenge_len; + char *secret; + int secret_len; + MS_ChapResponse *response; +{ + int i; + MD4_CTX md4Context; + u_char hash[MD4_SIGNATURE_SIZE]; + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + + /* Initialize the Unicode version of the secret (== password). */ + /* This implicitly supports 8-bit ISO8859/1 characters. */ + BZERO(unicodePassword, sizeof(unicodePassword)); + for (i = 0; i < secret_len; i++) + unicodePassword[i * 2] = (u_char)secret[i]; + + MD4Init(&md4Context); + MD4Update(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */ + + MD4Final(hash, &md4Context); /* Tell MD4 we're done */ + + ChallengeResponse(rchallenge, hash, response->NTResp); +} + +#ifdef MSLANMAN +static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ + +static void +ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response) + char *rchallenge; + int rchallenge_len; + char *secret; + int secret_len; + MS_ChapResponse *response; +{ + int i; + u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + + /* LANMan password is case insensitive */ + BZERO(UcasePassword, sizeof(UcasePassword)); + for (i = 0; i < secret_len; i++) + UcasePassword[i] = (u_char)toupper(secret[i]); + DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); + DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); + ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); +} +#endif + +void +ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len) + chap_state *cstate; + char *rchallenge; + int rchallenge_len; + char *secret; + int secret_len; +{ + MS_ChapResponse response; +#ifdef MSLANMAN + extern int ms_lanman; +#endif + +#if 0 + CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret)); +#endif + BZERO(&response, sizeof(response)); + + /* Calculate both always */ + ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response); + +#ifdef MSLANMAN + ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response); + + /* prefered method is set by option */ + response.UseNT = !ms_lanman; +#else + response.UseNT = 1; +#endif + + BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); + cstate->resp_length = MS_CHAP_RESPONSE_LEN; +} + +#endif /* CHAPMS */ diff --git a/cpukit/pppd/chap_ms.h b/cpukit/pppd/chap_ms.h new file mode 100644 index 0000000000..fad2755c86 --- /dev/null +++ b/cpukit/pppd/chap_ms.h @@ -0,0 +1,33 @@ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +#ifndef __CHAPMS_INCLUDE__ + +#define MD4_SIGNATURE_SIZE 16 /* 16 bytes in a MD4 message digest */ +#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */ + +void ChapMS __P((chap_state *, char *, int, char *, int)); + +#define __CHAPMS_INCLUDE__ +#endif /* __CHAPMS_INCLUDE__ */ diff --git a/cpukit/pppd/chat.c b/cpukit/pppd/chat.c new file mode 100644 index 0000000000..ad4c0c932e --- /dev/null +++ b/cpukit/pppd/chat.c @@ -0,0 +1,1004 @@ +/* + * Chat -- a program for automatic session establishment (i.e. dial + * the phone and log in). + * + * Standard termination codes: + * 0 - successful completion of the script + * 1 - invalid argument, expect string too large, etc. + * 2 - error on an I/O operation or fatal error condition. + * 3 - timeout waiting for a simple string. + * 4 - the first string declared as "ABORT" + * 5 - the second string declared as "ABORT" + * 6 - ... and so on for successive ABORT strings. + * + * This software is in the public domain. + * + * ----------------- + * added -T and -U option and \T and \U substitution to pass a phone + * number into chat script. Two are needed for some ISDN TA applications. + * Keith Dart + * + * + * Added SAY keyword to send output to stderr. + * This allows to turn ECHO OFF and to output specific, user selected, + * text to give progress messages. This best works when stderr + * exists (i.e.: pppd in nodetach mode). + * + * Added HANGUP directives to allow for us to be called + * back. When HANGUP is set to NO, chat will not hangup at HUP signal. + * We rely on timeouts in that case. + * + * Added CLR_ABORT to clear previously set ABORT string. This has been + * dictated by the HANGUP above as "NO CARRIER" (for example) must be + * an ABORT condition until we know the other host is going to close + * the connection for call back. As soon as we have completed the + * first stage of the call back sequence, "NO CARRIER" is a valid, non + * fatal string. As soon as we got called back (probably get "CONNECT"), + * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command. + * Note that CLR_ABORT packs the abort_strings[] array so that we do not + * have unused entries not being reclaimed. + * + * In the same vein as above, added CLR_REPORT keyword. + * + * Allow for comments. Line starting with '#' are comments and are + * ignored. If a '#' is to be expected as the first character, the + * expect string must be quoted. + * + * + * Francis Demierre + * Thu May 15 17:15:40 MET DST 1997 + * + * + * Added -r "report file" switch & REPORT keyword. + * Robert Geer + * + * Added -s "use stderr" and -S "don't use syslog" switches. + * June 18, 1997 + * Karl O. Pinc + * + * + * Added -e "echo" switch & ECHO keyword + * Dick Streefland + * + * + * Considerable updates and modifications by + * Al Longyear + * Paul Mackerras + * + * + * The original author is: + * + * Karl Fox + * Morning Star Technologies, Inc. + * 1760 Zollinger Road + * Columbus, OH 43221 + * (614)451-1883 + * + * + */ + +#ifndef lint +/* static char rcsid[] = ""; */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef TERMIOS +#define TERMIOS + + +#include + +#define STR_LEN 1024 +char temp2[STR_LEN]; + +#ifndef SIGTYPE +#define SIGTYPE void +#endif + +#undef __P +#undef __V + +#ifdef __STDC__ +#include +#define __V(x) x +#define __P(x) x +#else +#include +#define __V(x) (va_alist) va_dcl +#define __P(x) () +#define const +#endif + +#ifndef O_NONBLOCK +#define O_NONBLOCK O_NDELAY +#endif + + +/*************** Micro getopt() *********************************************/ +#define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \ + (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\ + &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0)) +#define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \ + (_O=4,(char*)0):(char*)0) +#define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0) +#define ARG(c,v) (c?(--c,*v++):(char*)0) + +#if 0 +static int _O = 0; /* Internal state */ +#endif +/*************** Micro getopt() *********************************************/ + +char *program_name; + +#define MAX_ABORTS 5 +#define MAX_REPORTS 5 +#define DEFAULT_CHAT_TIMEOUT 45 +#define fcntl(a, b,c ) 0 +#define MAX_TIMEOUTS 10 + +int echo = 0; +int verbose = 0; +int to_log = 1; +int to_stderr = 0; +int Verbose = 0; +int quiet = 0; +int report = 0; +int exit_code = 0; +static int speed=0; +char *report_file = (char *) 0; +char *chat_file = (char *) 0; +char *phone_num = (char *) 0; +char *phone_num2 = (char *) 0; +int chat_timeout = DEFAULT_CHAT_TIMEOUT; +static int timeout = DEFAULT_CHAT_TIMEOUT; +int have_tty_parameters = 0; + +#ifdef TERMIOS +#define term_parms struct termios +#define get_term_param(param) tcgetattr(modem_fd, param) +#define set_term_param(param) tcsetattr(modem_fd, TCSANOW, param) +struct termios saved_tty_parameters; +#endif + +char *abort_string[MAX_ABORTS]={"BUSY","NO DIALTONE","NO CARRIER","NO ASWER","RINGING\r\n\r\nRINGING"}; +char *fail_reason = (char *)0, + fail_buffer[50]; +int n_aborts = MAX_ABORTS, abort_next = 0, timeout_next = 0, echo_next = 0; +int clear_abort_next = 0; + +char *report_string[MAX_REPORTS] ; +char report_buffer[50] ; +int n_reports = 0, report_next = 0, report_gathering = 0 ; +int clear_report_next = 0; + +int say_next = 0, hup_next = 0; + +void *dup_mem __P((void *b, size_t c)); +void *copy_of __P((char *s)); +/* +SIGTYPE sigalrm __P((int signo)); +SIGTYPE sigint __P((int signo)); +SIGTYPE sigterm __P((int signo)); +SIGTYPE sighup __P((int signo)); +*/ +void unalarm __P((void)); +void init __P((void)); +void set_tty_parameters __P((void)); +void echo_stderr __P((int)); +void break_sequence __P((void)); +void terminate __P((int status)); +void do_file __P((char *chat_file)); +int get_string __P((register char *string)); +int put_string __P((register char *s)); +int write_char __P((int c)); +int put_char __P((int c)); +int get_char __P((void)); +void chat_send __P((register char *s)); +char *character __P((int c)); +void chat_expect __P((register char *s)); +char *clean __P((register char *s, int sending)); +void break_sequence __P((void)); +void terminate __P((int status)); +void pack_array __P((char **array, int end)); +char *expect_strtok __P((char *, char *)); +int vfmtmsg __P((char *, int, const char *, va_list)); /* vsprintf++ */ + +#if 0 +int usleep( long usec ); /* returns 0 if ok, else -1 */ +#endif + +extern int input_fd,output_fd; + +int main __P((int, char *[])); + +void *dup_mem(b, c) +void *b; +size_t c; +{ + void *ans = malloc (c); + if (!ans) + return NULL; + + memcpy (ans, b, c); + return ans; +} + +void *copy_of (s) +char *s; +{ + return dup_mem (s, strlen (s) + 1); +} + +/* + * chat [ -v ] [-T number] [-U number] [ -t timeout ] [ -f chat-file ] \ + * [ -r report-file ] \ + * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]] + * + * Perform a UUCP-dialer-like chat script on stdin and stdout. + */ +char *getnextcommand(char **string) +{ + char *buf=*string,*res; + res=strchr(buf,'@'); + if (res==NULL) + return NULL; + *res='\0'; + *string=res+1; + return buf; +} + + +extern int modem_fd; +int + chatmain(argv) + char *argv; +{ + char *arg; + int i; + char *t; + exit_code=0; + speed=0; +/* + * Default the report file to the stderr location + */ +/* if (report_fp == NULL) + report_fp = stderr; +*/ + init(); + while ( (arg = getnextcommand(&argv)) != NULL) { + chat_expect(arg); + if (exit_code>0) break; + t=temp2; + + while(*t) + { + if (strncmp("CARRIER",t,7)==0) + {/* parse speed information */ + i=0; + while(!isdigit(t[i])) + i++; + t=&t[i]; + i=0; + while(isdigit(t[i])) + i++; + t[i]=0; + sscanf(t,"%d",&speed); + break; + } + t++; + } + if ((arg = getnextcommand(&argv)) != NULL) + chat_send(arg); + if (exit_code>0) break; + } + + if (exit_code) return exit_code; + return -speed; +} + + + +/* + * Print an error message and terminate. + */ + +void init() +{ + set_tty_parameters(); + speed=0; +} + +void set_tty_parameters() +{ + term_parms t; + + if (get_term_param (&t) < 0) + syslog(LOG_NOTICE,"Can't get terminal parameters:") + ; + + saved_tty_parameters = t; + have_tty_parameters = 1; + t.c_iflag |= IGNBRK | ISTRIP | IGNPAR; + t.c_oflag = 0; + t.c_lflag = 0; + t.c_cc[VERASE] = + t.c_cc[VKILL] = 0; + t.c_cc[VMIN] = 0; + t.c_cc[VTIME] = 1; + if (set_term_param (&t) < 0) + syslog(LOG_NOTICE,"Can't set terminal parameters:") + ; +} + +void break_sequence() +{ + +/* tcsendbreak (0, 0);*/ +} + +/*void terminate(status) +int status; +{ + echo_stderr(-1); + + if (have_tty_parameters) { + if (set_term_param (&saved_tty_parameters) < 0) + fatal(2, "Can't restore terminal parameters: %m"); + } +} +*/ +/* + * 'Clean up' this string. + */ +char *clean(s, sending) +register char *s; +int sending; /* set to 1 when sending (putting) this string. */ +{ + char temp[STR_LEN], cur_chr; + register char *s1, *phchar; + int add_return = sending; +#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) + + s1 = temp; + while (*s) { + cur_chr = *s++; + if (cur_chr == '^') { + cur_chr = *s++; + if (cur_chr == '\0') { + *s1++ = '^'; + break; + } + cur_chr &= 0x1F; + if (cur_chr != 0) { + *s1++ = cur_chr; + } + continue; + } + + if (cur_chr != '\\') { + *s1++ = cur_chr; + continue; + } + + cur_chr = *s++; + if (cur_chr == '\0') { + if (sending) { + *s1++ = '\\'; + *s1++ = '\\'; + } + break; + } + + switch (cur_chr) { + case 'b': + *s1++ = '\b'; + break; + + case 'c': + if (sending && *s == '\0') + add_return = 0; + else + *s1++ = cur_chr; + break; + + case '\\': + case 'K': + case 'p': + case 'd': + if (sending) + *s1++ = '\\'; + + *s1++ = cur_chr; + break; + + case 'T': + if (sending && phone_num) { + for ( phchar = phone_num; *phchar != '\0'; phchar++) + *s1++ = *phchar; + } + else { + *s1++ = '\\'; + *s1++ = 'T'; + } + break; + + case 'U': + if (sending && phone_num2) { + for ( phchar = phone_num2; *phchar != '\0'; phchar++) + *s1++ = *phchar; + } + else { + *s1++ = '\\'; + *s1++ = 'U'; + } + break; + + case 'q': + quiet = 1; + break; + + case 'r': + *s1++ = '\r'; + break; + + case 'n': + *s1++ = '\n'; + break; + + case 's': + *s1++ = ' '; + break; + + case 't': + *s1++ = '\t'; + break; + + case 'N': + if (sending) { + *s1++ = '\\'; + *s1++ = '\0'; + } + else + *s1++ = 'N'; + break; + + default: + if (isoctal (cur_chr)) { + cur_chr &= 0x07; + if (isoctal (*s)) { + cur_chr <<= 3; + cur_chr |= *s++ - '0'; + if (isoctal (*s)) { + cur_chr <<= 3; + cur_chr |= *s++ - '0'; + } + } + + if (cur_chr != 0 || sending) { + if (sending && (cur_chr == '\\' || cur_chr == 0)) + *s1++ = '\\'; + *s1++ = cur_chr; + } + break; + } + + if (sending) + *s1++ = '\\'; + *s1++ = cur_chr; + break; + } + } + + if (add_return) + *s1++ = '\r'; + + *s1++ = '\0'; /* guarantee closure */ + *s1++ = '\0'; /* terminate the string */ + return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */ +} + +/* + * A modified version of 'strtok'. This version skips \ sequences. + */ + +char *expect_strtok (s, term) + char *s, *term; +{ + static char *str = ""; + int escape_flag = 0; + char *result; + +/* + * If a string was specified then do initial processing. + */ + if (s) + str = s; + +/* + * If this is the escape flag then reset it and ignore the character. + */ + if (*str) + result = str; + else + result = (char *) 0; + + while (*str) { + if (escape_flag) { + escape_flag = 0; + ++str; + continue; + } + + if (*str == '\\') { + ++str; + escape_flag = 1; + continue; + } + +/* + * If this is not in the termination string, continue. + */ + if (strchr (term, *str) == (char *) 0) { + ++str; + continue; + } + +/* + * This is the terminator. Mark the end of the string and stop. + */ + *str++ = '\0'; + break; + } + return (result); +} + +/* + * Process the expect string + */ + +void chat_expect (s) +char *s; +{ + char *expect; + char *reply; + + if (strcmp(s, "HANGUP") == 0) { + ++hup_next; + return ; + } + + if (strcmp(s, "ABORT") == 0) { + ++abort_next; + return ; + } + + if (strcmp(s, "CLR_ABORT") == 0) { + ++clear_abort_next; + return ; + } + + if (strcmp(s, "REPORT") == 0) { + ++report_next; + return ; + } + + if (strcmp(s, "CLR_REPORT") == 0) { + ++clear_report_next; + return ; + } + + if (strcmp(s, "TIMEOUT") == 0) { + ++timeout_next; + return ; + } + + if (strcmp(s, "ECHO") == 0) { + ++echo_next; + return ; + } + + if (strcmp(s, "SAY") == 0) { + ++say_next; + return ; + } + +/* + * Fetch the expect and reply string. + */ + for (;;) { + expect = expect_strtok (s, "-"); + s = (char *)0 ; + + if (expect == (char *) 0) + return ; + + reply = expect_strtok (s, "-"); + +/* + * Handle the expect string. If successful then exit. + */ + if (get_string (expect)) + return; + +/* + * If there is a sub-reply string then send it. Otherwise any condition + * is terminal. + */ + if (reply == (char *) 0 || exit_code != 3) + break; + + chat_send (reply); + } + +/* + * The expectation did not occur. This is terminal. + */ + return ; +} + +/* + * process the reply string + */ +void chat_send (s) +register char *s; +{ + if (say_next) { + say_next = 0; + s = clean(s,0); + write(modem_fd, s, strlen(s)); + free(s); + return; + } + + if (hup_next) { + hup_next = 0; + } + + if (echo_next) { + echo_next = 0; + echo = (strcmp(s, "ON") == 0); + return; + } + + if (abort_next) { + /* char *s1; */ + + + ; + + + return; + } + + +/* if (report_next) { + char *s1; + + report_next = 0; + if (n_reports >= MAX_REPORTS) + { + exit_code=2; + return; + } + + s1 = clean(s, 0); + + if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) + { + exit_code=1; + return; + } + + report_string[n_reports++] = s1; + + return; + } +*/ +/* if (clear_report_next) { + char *s1; + int i; + int old_max; + int pack = 0; + + clear_report_next = 0; + + s1 = clean(s, 0); + + if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) + { + exit_code=1; + return; + } + + old_max = n_reports; + for (i=0; i < n_reports; i++) { + if ( strcmp(s1,report_string[i]) == 0 ) { + free(report_string[i]); + report_string[i] = NULL; + pack++; + n_reports--; + } + } + free(s1); + if (pack) + pack_array(report_string,old_max); + + return; + } +*/ + + if (timeout_next) { + timeout=atoi(s); + timeout_next = 0; + chat_timeout = atoi(s); + + if (chat_timeout <= 0) + chat_timeout = DEFAULT_CHAT_TIMEOUT; + + + return; + } + if (strcmp(s, "EOT") == 0) + s = "^D\\c"; + else if (strcmp(s, "BREAK") == 0) + s = "\\K\\c"; + + if (!put_string(s)) + { + exit_code=1; + return; + } +} + +int get_char() +{ + int status; + char c; + int tries=MAX_TIMEOUTS; + + while(tries) + { + status = read(modem_fd, &c, 1); + switch (status) { + case 1: + return ((int)c & 0x7F); + default: + tries--; + } + } + return -1; +} + +int put_char(c) +int c; +{ + int status; + char ch = c; + + /* inter-character typing delay (?) */ + + status = write(modem_fd, &ch, 1); + + switch (status) { + case 1: + return (0); + + default: + + + } + return 0; +} + +int write_char (c) +int c; +{ + if (put_char(c) < 0) { + return (0); + } + return (1); +} + +int put_string (s) +register char *s; +{ + + + quiet = 0; + s = clean(s, 1); + while (*s) { + register char c = *s++; + + if (c != '\\') { + if (!write_char (c)) + return 0; + continue; + } + + c = *s++; + switch (c) { + case 'd': + sleep(1); + break; + + case 'K': + break_sequence(); + break; + + case 'p': + usleep(10000); /* 1/100th of a second (arg is microseconds) */ + break; + + default: + if (!write_char (c)) + return 0; + break; + } + } + + /* alarm(0);*/ + return (1); +} + +/* + * Echo a character to stderr. + * When called with -1, a '\n' character is generated when + * the cursor is not at the beginning of a line. + */ +void echo_stderr(n) +int n; +{ +/* static int need_lf; + char *s; + + switch (n) { + case '\r': + break; + case -1: + if (need_lf == 0) + break; + + case '\n': + write(2, "\n", 1); + need_lf = 0; + break; + default: + s = character(n); + write(2, s, strlen(s)); + need_lf = 1; + break; + }*/ +} + +/* + * 'Wait for' this string to appear on this file descriptor. + */ + +int get_string(string) +register char *string; +{ + int c, len, minlen; + register char *s = temp2, *end = s + STR_LEN; + char *logged = temp2; + struct termios tios; + + tcgetattr(modem_fd, &tios); + tios.c_cc[VMIN] = 0; + tios.c_cc[VTIME] = timeout*10/MAX_TIMEOUTS; + tcsetattr(modem_fd, TCSANOW, &tios); + + string = clean(string, 0); + len = strlen(string); + minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1; + + if (len > STR_LEN) { + exit_code = 1; + return 0; + } + + if (len == 0) { + return (1); + } + + + while ( (c = get_char()) >= 0) { + int n, abort_len; + + *s++ = c; + *s=0; + + if (s - temp2 >= len && + c == string[len - 1] && + strncmp(s - len, string, len) == 0) { + return (1); + } + + for (n = 0; n < n_aborts; ++n) { + if (s - temp2 >= (abort_len = strlen(abort_string[n])) && + strncmp(s - abort_len, abort_string[n], abort_len) == 0) { + + exit_code = n + 4; + strcpy(fail_reason = fail_buffer, abort_string[n]); + return (0); + } + } + + if (s >= end) { + if (logged < s - minlen) { + logged = s; + } + s -= minlen; + memmove(temp2, s, minlen); + logged = temp2 + (logged - s); + s = temp2 + minlen; + } + } + exit_code = 3; + return (0); +} + +/* + * Gross kludge to handle Solaris versions >= 2.6 having usleep. + */ + +/* + usleep -- support routine for 4.2BSD system call emulations + last edit: 29-Oct-1984 D A Gwyn + */ + + +#if 0 +int +usleep( usec ) /* returns 0 if ok, else -1 */ + long usec; /* delay in microseconds */ +{ + rtems_status_code status; + rtems_interval ticks_per_second; + rtems_interval ticks; + status = rtems_clock_get( + RTEMS_CLOCK_GET_TICKS_PER_SECOND, + &ticks_per_second); + ticks = (usec * (ticks_per_second/1000))/1000; + status = rtems_task_wake_after( ticks ); + return 0; +} +#endif + +void pack_array (array, end) + char **array; /* The address of the array of string pointers */ + int end; /* The index of the next free entry before CLR_ */ +{ + int i, j; + + for (i = 0; i < end; i++) { + if (array[i] == NULL) { + for (j = i+1; j < end; ++j) + if (array[j] != NULL) + array[i++] = array[j]; + for (; i < end; ++i) + array[i] = NULL; + break; + } + } +} + +/* + * vfmtmsg - format a message into a buffer. Like vsprintf except we + * also specify the length of the output buffer, and we handle the + * %m (error message) format. + * Doesn't do floating-point formats. + * Returns the number of chars put into buf. + */ +#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) diff --git a/cpukit/pppd/demand.c b/cpukit/pppd/demand.c new file mode 100644 index 0000000000..a1910f258f --- /dev/null +++ b/cpukit/pppd/demand.c @@ -0,0 +1,348 @@ +/* + * demand.c - Support routines for demand-dialling. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +/* static char rcsid[] = "$Id$"; */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef PPP_FILTER +#include +#include +#include +#endif + +#include "pppd.h" +#include "fsm.h" +#include "ipcp.h" +#include "lcp.h" + +char *frame; +int framelen; +int framemax; +int escape_flag; +int flush_flag; +int fcs; + +struct packet { + int length; + struct packet *next; + unsigned char data[1]; +}; + +struct packet *pend_q; +struct packet *pend_qtail; + +static int active_packet __P((unsigned char *, int)); + +/* + * demand_conf - configure the interface for doing dial-on-demand. + */ +void +demand_conf() +{ + int i; + struct protent *protp; + +/* framemax = lcp_allowoptions[0].mru; + if (framemax < PPP_MRU) */ + framemax = PPP_MRU; + framemax += PPP_HDRLEN + PPP_FCSLEN; + frame = malloc(framemax); + if (frame == NULL) + novm("demand frame"); + framelen = 0; + pend_q = NULL; + escape_flag = 0; + flush_flag = 0; + fcs = PPP_INITFCS; + + ppp_send_config(0, PPP_MRU, (u_int32_t) 0, 0, 0); + ppp_recv_config(0, PPP_MRU, (u_int32_t) 0, 0, 0); + +#ifdef PPP_FILTER + set_filters(&pass_filter, &active_filter); +#endif + + /* + * Call the demand_conf procedure for each protocol that's got one. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->enabled_flag && protp->demand_conf != NULL) + if (!((*protp->demand_conf)(0))) + die(1); +} + + +/* + * demand_block - set each network protocol to block further packets. + */ +void +demand_block() +{ + int i; + struct protent *protp; + + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->enabled_flag && protp->demand_conf != NULL) + sifnpmode(0, protp->protocol & ~0x8000, NPMODE_QUEUE); + get_loop_output(); +} + +/* + * demand_discard - set each network protocol to discard packets + * with an error. + */ +void +demand_discard() +{ + struct packet *pkt, *nextpkt; + int i; + struct protent *protp; + + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->enabled_flag && protp->demand_conf != NULL) + sifnpmode(0, protp->protocol & ~0x8000, NPMODE_ERROR); + get_loop_output(); + + /* discard all saved packets */ + for (pkt = pend_q; pkt != NULL; pkt = nextpkt) { + nextpkt = pkt->next; + free(pkt); + } + pend_q = NULL; + framelen = 0; + flush_flag = 0; + escape_flag = 0; + fcs = PPP_INITFCS; +} + +/* + * demand_unblock - set each enabled network protocol to pass packets. + */ +void +demand_unblock() +{ + int i; + struct protent *protp; + + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->enabled_flag && protp->demand_conf != NULL) + sifnpmode(0, protp->protocol & ~0x8000, NPMODE_PASS); +} + +/* + * FCS lookup table as calculated by genfcstab. + */ +static u_short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +/* + * loop_chars - process characters received from the loopback. + * Calls loop_frame when a complete frame has been accumulated. + * Return value is 1 if we need to bring up the link, 0 otherwise. + */ +int +loop_chars(p, n) + unsigned char *p; + int n; +{ + int c, rv; + + rv = 0; + for (; n > 0; --n) { + c = *p++; + if (c == PPP_FLAG) { + if (!escape_flag && !flush_flag + && framelen > 2 && fcs == PPP_GOODFCS) { + framelen -= 2; + if (loop_frame(frame, framelen)) + rv = 1; + } + framelen = 0; + flush_flag = 0; + escape_flag = 0; + fcs = PPP_INITFCS; + continue; + } + if (flush_flag) + continue; + if (escape_flag) { + c ^= PPP_TRANS; + escape_flag = 0; + } else if (c == PPP_ESCAPE) { + escape_flag = 1; + continue; + } + if (framelen >= framemax) { + flush_flag = 1; + continue; + } + frame[framelen++] = c; + fcs = PPP_FCS(fcs, c); + } + return rv; +} + +/* + * loop_frame - given a frame obtained from the loopback, + * decide whether to bring up the link or not, and, if we want + * to transmit this frame later, put it on the pending queue. + * Return value is 1 if we need to bring up the link, 0 otherwise. + * We assume that the kernel driver has already applied the + * pass_filter, so we won't get packets it rejected. + * We apply the active_filter to see if we want this packet to + * bring up the link. + */ +int +loop_frame(frame, len) + unsigned char *frame; + int len; +{ + struct packet *pkt; + + /* log_packet(frame, len, "from loop: ", LOG_DEBUG); */ + if (len < PPP_HDRLEN) + return 0; + if ((PPP_PROTOCOL(frame) & 0x8000) != 0) + return 0; /* shouldn't get any of these anyway */ + if (!active_packet(frame, len)) + return 0; + + pkt = (struct packet *) malloc(sizeof(struct packet) + len); + if (pkt != NULL) { + pkt->length = len; + pkt->next = NULL; + memcpy(pkt->data, frame, len); + if (pend_q == NULL) + pend_q = pkt; + else + pend_qtail->next = pkt; + pend_qtail = pkt; + } + return 1; +} + +/* + * demand_rexmit - Resend all those frames which we got via the + * loopback, now that the real serial link is up. + */ +void +demand_rexmit(proto) + int proto; +{ + struct packet *pkt, *prev, *nextpkt; + + prev = NULL; + pkt = pend_q; + pend_q = NULL; + for (; pkt != NULL; pkt = nextpkt) { + nextpkt = pkt->next; + if (PPP_PROTOCOL(pkt->data) == proto) { + output(0, pkt->data, pkt->length); + free(pkt); + } else { + if (prev == NULL) + pend_q = pkt; + else + prev->next = pkt; + prev = pkt; + } + } + pend_qtail = prev; + if (prev != NULL) + prev->next = NULL; +} + +/* + * Scan a packet to decide whether it is an "active" packet, + * that is, whether it is worth bringing up the link for. + */ +static int +active_packet(p, len) + unsigned char *p; + int len; +{ + int proto, i; + struct protent *protp; + + if (len < PPP_HDRLEN) + return 0; + proto = PPP_PROTOCOL(p); +#ifdef PPP_FILTER + if (active_filter.bf_len != 0 + && bpf_filter(active_filter.bf_insns, frame, len, len) == 0) + return 0; +#endif + for (i = 0; (protp = protocols[i]) != NULL; ++i) { + if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) { + if (!protp->enabled_flag) + return 0; + if (protp->active_pkt == NULL) + return 1; + return (*protp->active_pkt)(p, len); + } + } + return 0; /* not a supported protocol !!?? */ +} diff --git a/cpukit/pppd/fsm.c b/cpukit/pppd/fsm.c new file mode 100644 index 0000000000..aeb4564b98 --- /dev/null +++ b/cpukit/pppd/fsm.c @@ -0,0 +1,798 @@ +/* + * fsm.c - {Link, IP} Control Protocol Finite State Machine. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +/* static char rcsid[] = "$Id$"; */ +#endif +#define log_packet(p, len, prefix, level) +/* + * TODO: + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#include +#include +#include +#include + +#include "pppd.h" +#include "fsm.h" + +static void fsm_timeout __P((void *)); +static void fsm_rconfreq __P((fsm *, int, u_char *, int)); +static void fsm_rconfack __P((fsm *, int, u_char *, int)); +static void fsm_rconfnakrej __P((fsm *, int, int, u_char *, int)); +static void fsm_rtermreq __P((fsm *, int, u_char *, int)); +static void fsm_rtermack __P((fsm *)); +static void fsm_rcoderej __P((fsm *, u_char *, int)); +static void fsm_sconfreq __P((fsm *, int)); + +#define PROTO_NAME(f) ((f)->callbacks->proto_name) + +int peer_mru[NUM_PPP]; + + +/* + * fsm_init - Initialize fsm. + * + * Initialize fsm state. + */ +void +fsm_init(f) + fsm *f; +{ + f->state = INITIAL; + f->flags = 0; + f->id = 0; /* XXX Start with random id? */ + f->timeouttime = DEFTIMEOUT; + f->maxconfreqtransmits = DEFMAXCONFREQS; + f->maxtermtransmits = DEFMAXTERMREQS; + f->maxnakloops = DEFMAXNAKLOOPS; + f->term_reason_len = 0; +} + + +/* + * fsm_lowerup - The lower layer is up. + */ +void +fsm_lowerup(f) + fsm *f; +{ + switch( f->state ){ + case INITIAL: + f->state = CLOSED; + break; + + case STARTING: + if( f->flags & OPT_SILENT ) + f->state = STOPPED; + else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + } + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Up event in state %d!", + PROTO_NAME(f), f->state)); + } +} + + +/* + * fsm_lowerdown - The lower layer is down. + * + * Cancel all timeouts and inform upper layers. + */ +void +fsm_lowerdown(f) + fsm *f; +{ + switch( f->state ){ + case CLOSED: + f->state = INITIAL; + break; + + case STOPPED: + f->state = STARTING; + if( f->callbacks->starting ) + (*f->callbacks->starting)(f); + break; + + case CLOSING: + f->state = INITIAL; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case STOPPING: + case REQSENT: + case ACKRCVD: + case ACKSENT: + f->state = STARTING; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case OPENED: + if( f->callbacks->down ) + (*f->callbacks->down)(f); + f->state = STARTING; + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Down event in state %d!", + PROTO_NAME(f), f->state)); + } +} + + +/* + * fsm_open - Link is allowed to come up. + */ +void +fsm_open(f) + fsm *f; +{ + switch( f->state ){ + case INITIAL: + f->state = STARTING; + if( f->callbacks->starting ) + (*f->callbacks->starting)(f); + break; + + case CLOSED: + if( f->flags & OPT_SILENT ) + f->state = STOPPED; + else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + } + break; + + case CLOSING: + f->state = STOPPING; + /* fall through */ + case STOPPED: + case OPENED: + if( f->flags & OPT_RESTART ){ + fsm_lowerdown(f); + fsm_lowerup(f); + } + break; + } +} + + +/* + * fsm_close - Start closing connection. + * + * Cancel timeouts and either initiate close or possibly go directly to + * the CLOSED state. + */ +void +fsm_close(f, reason) + fsm *f; + char *reason; +{ + f->term_reason = reason; + f->term_reason_len = (reason == NULL? 0: strlen(reason)); + switch( f->state ){ + case STARTING: + f->state = INITIAL; + break; + case STOPPED: + f->state = CLOSED; + break; + case STOPPING: + f->state = CLOSING; + break; + + case REQSENT: + case ACKRCVD: + case ACKSENT: + case OPENED: + if( f->state != OPENED ) + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + else if( f->callbacks->down ) + (*f->callbacks->down)(f); /* Inform upper layers we're down */ + + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = CLOSING; + break; + } +} + + +/* + * fsm_timeout - Timeout expired. + */ +static void +fsm_timeout(arg) + void *arg; +{ + fsm *f = (fsm *) arg; + + switch (f->state) { + case CLOSING: + case STOPPING: + if( f->retransmits <= 0 ){ + /* + * We've waited for an ack long enough. Peer probably heard us. + */ + f->state = (f->state == CLOSING)? CLOSED: STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + } else { + /* Send Terminate-Request */ + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + } + break; + + case REQSENT: + case ACKRCVD: + case ACKSENT: + if (f->retransmits <= 0) { + syslog(LOG_WARNING, "%s: timeout sending Config-Requests", + PROTO_NAME(f)); + f->state = STOPPED; + if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) + (*f->callbacks->finished)(f); + + } else { + /* Retransmit the configure-request */ + if (f->callbacks->retransmit) + (*f->callbacks->retransmit)(f); + fsm_sconfreq(f, 1); /* Re-send Configure-Request */ + if( f->state == ACKRCVD ) + f->state = REQSENT; + } + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Timeout event in state %d!", + PROTO_NAME(f), f->state)); + } +} + + +/* + * fsm_input - Input packet. + */ +void +fsm_input(f, inpacket, l) + fsm *f; + u_char *inpacket; + int l; +{ + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < HEADERLEN) { + FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.", + f->protocol)); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < HEADERLEN) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.", + f->protocol)); + return; + } + if (len > l) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.", + f->protocol)); + return; + } + len -= HEADERLEN; /* subtract header length */ + + if( f->state == INITIAL || f->state == STARTING ){ + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d.", + f->protocol, f->state)); + return; + } + + /* + * Action depends on code. + */ + switch (code) { + case CONFREQ: + fsm_rconfreq(f, id, inp, len); + break; + + case CONFACK: + fsm_rconfack(f, id, inp, len); + break; + + case CONFNAK: + case CONFREJ: + fsm_rconfnakrej(f, code, id, inp, len); + break; + + case TERMREQ: + fsm_rtermreq(f, id, inp, len); + break; + + case TERMACK: + fsm_rtermack(f); + break; + + case CODEREJ: + fsm_rcoderej(f, inp, len); + break; + + default: + if( !f->callbacks->extcode + || !(*f->callbacks->extcode)(f, code, id, inp, len) ) + fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); + break; + } +} + + +/* + * fsm_rconfreq - Receive Configure-Request. + */ +static void +fsm_rconfreq(f, id, inp, len) + fsm *f; + u_char id; + u_char *inp; + int len; +{ + int code, reject_if_disagree; + + FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d.", PROTO_NAME(f), id)); + switch( f->state ){ + case CLOSED: + /* Go away, we're closed */ + fsm_sdata(f, TERMACK, id, NULL, 0); + return; + case CLOSING: + case STOPPING: + return; + + case OPENED: + /* Go down and restart negotiation */ + if( f->callbacks->down ) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + break; + + case STOPPED: + /* Negotiation started by our peer */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = REQSENT; + break; + } + + /* + * Pass the requested configuration options + * to protocol-specific code for checking. + */ + if (f->callbacks->reqci){ /* Check CI */ + reject_if_disagree = (f->nakloops >= f->maxnakloops); + code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); + } else if (len) + code = CONFREJ; /* Reject all CI */ + else + code = CONFACK; + + /* send the Ack, Nak or Rej to the peer */ + fsm_sdata(f, code, id, inp, len); + + if (code == CONFACK) { + if (f->state == ACKRCVD) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = OPENED; + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + } else + f->state = ACKSENT; + f->nakloops = 0; + + } else { + /* we sent CONFACK or CONFREJ */ + if (f->state != ACKRCVD) + f->state = REQSENT; + if( code == CONFNAK ) + ++f->nakloops; + } +} + + +/* + * fsm_rconfack - Receive Configure-Ack. + */ +static void +fsm_rconfack(f, id, inp, len) + fsm *f; + int id; + u_char *inp; + int len; +{ + FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d.", + PROTO_NAME(f), id)); + + if (id != f->reqid || f->seen_ack) /* Expected id? */ + return; /* Nope, toss... */ + if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): + (len == 0)) ){ + /* Ack is bad - ignore it */ + log_packet(inp, len, "Received bad configure-ack: ", LOG_ERR); + FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)", + PROTO_NAME(f), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case CLOSED: + case STOPPED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case REQSENT: + f->state = ACKRCVD; + f->retransmits = f->maxconfreqtransmits; + break; + + case ACKRCVD: + /* Huh? an extra valid Ack? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + break; + + case ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = OPENED; + f->retransmits = f->maxconfreqtransmits; + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + break; + + case OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = REQSENT; + break; + } +} + + +/* + * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. + */ +static void +fsm_rconfnakrej(f, code, id, inp, len) + fsm *f; + int code, id; + u_char *inp; + int len; +{ + int (*proc) __P((fsm *, u_char *, int)); + int ret; + + FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d.", + PROTO_NAME(f), id)); + + if (id != f->reqid || f->seen_ack) /* Expected id? */ + return; /* Nope, toss... */ + proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; + if (!proc || !(ret = proc(f, inp, len))) { + /* Nak/reject is bad - ignore it */ + log_packet(inp, len, "Received bad configure-nak/rej: ", LOG_ERR); + FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)", + PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case CLOSED: + case STOPPED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case REQSENT: + case ACKSENT: + /* They didn't agree to what we wanted - try another request */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + if (ret < 0) + f->state = STOPPED; /* kludge for stopping CCP */ + else + fsm_sconfreq(f, 0); /* Send Configure-Request */ + break; + + case ACKRCVD: + /* Got a Nak/reject when we had already had an Ack?? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = REQSENT; + break; + + case OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = REQSENT; + break; + } +} + + +/* + * fsm_rtermreq - Receive Terminate-Req. + */ +static void +fsm_rtermreq(f, id, p, len) + fsm *f; + int id; + u_char *p; + int len; +{ + char str[80]; + + FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d.", + PROTO_NAME(f), id)); + + switch (f->state) { + case ACKRCVD: + case ACKSENT: + f->state = REQSENT; /* Start over but keep trying */ + break; + + case OPENED: + if (len > 0) { + fmtmsg(str, sizeof(str), "%0.*v", len, p); + syslog(LOG_INFO, "%s terminated by peer (%s)", PROTO_NAME(f), str); + } else + syslog(LOG_INFO, "%s terminated by peer", PROTO_NAME(f)); + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + f->retransmits = 0; + f->state = STOPPING; + TIMEOUT(fsm_timeout, f, f->timeouttime); + break; + } + + fsm_sdata(f, TERMACK, id, NULL, 0); +} + + +/* + * fsm_rtermack - Receive Terminate-Ack. + */ +static void +fsm_rtermack(f) + fsm *f; +{ + FSMDEBUG((LOG_INFO, "fsm_rtermack(%s).", PROTO_NAME(f))); + + switch (f->state) { + case CLOSING: + UNTIMEOUT(fsm_timeout, f); + f->state = CLOSED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + case STOPPING: + UNTIMEOUT(fsm_timeout, f); + f->state = STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case ACKRCVD: + f->state = REQSENT; + break; + + case OPENED: + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); + break; + } +} + + +/* + * fsm_rcoderej - Receive an Code-Reject. + */ +static void +fsm_rcoderej(f, inp, len) + fsm *f; + u_char *inp; + int len; +{ + u_char code, id; + + FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s).", PROTO_NAME(f))); + + if (len < HEADERLEN) { + FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + syslog(LOG_WARNING, "%s: Rcvd Code-Reject for code %d, id %d", + PROTO_NAME(f), code, id); + + if( f->state == ACKRCVD ) + f->state = REQSENT; +} + + +/* + * fsm_protreject - Peer doesn't speak this protocol. + * + * Treat this as a catastrophic error (RXJ-). + */ +void +fsm_protreject(f) + fsm *f; +{ + switch( f->state ){ + case CLOSING: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case CLOSED: + f->state = CLOSED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case STOPPING: + case REQSENT: + case ACKRCVD: + case ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case STOPPED: + f->state = STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case OPENED: + if( f->callbacks->down ) + (*f->callbacks->down)(f); + + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = STOPPING; + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d!", + PROTO_NAME(f), f->state)); + } +} + + +/* + * fsm_sconfreq - Send a Configure-Request. + */ +static void +fsm_sconfreq(f, retransmit) + fsm *f; + int retransmit; +{ + u_char *outp; + int cilen; + + if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){ + /* Not currently negotiating - reset options */ + if( f->callbacks->resetci ) + (*f->callbacks->resetci)(f); + f->nakloops = 0; + } + + if( !retransmit ){ + /* New request - reset retransmission counter, use new ID */ + f->retransmits = f->maxconfreqtransmits; + f->reqid = ++f->id; + } + + f->seen_ack = 0; + + /* + * Make up the request packet + */ + outp = outpacket_buf + PPP_HDRLEN + HEADERLEN; + if( f->callbacks->cilen && f->callbacks->addci ){ + cilen = (*f->callbacks->cilen)(f); + if( cilen > peer_mru[f->unit] - HEADERLEN ) + cilen = peer_mru[f->unit] - HEADERLEN; + if (f->callbacks->addci) + (*f->callbacks->addci)(f, outp, &cilen); + } else + cilen = 0; + + /* send the request to our peer */ + fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); + + /* start the retransmit timer */ + --f->retransmits; + TIMEOUT(fsm_timeout, f, f->timeouttime); + + FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d", + PROTO_NAME(f), f->reqid)); +} + + +/* + * fsm_sdata - Send some data. + * + * Used for all packets sent to our peer by this module. + */ +void +fsm_sdata(f, code, id, data, datalen) + fsm *f; + u_char code, id; + u_char *data; + int datalen; +{ + u_char *outp; + int outlen; + + /* Adjust length to be smaller than MTU */ + outp = outpacket_buf; + if (datalen > peer_mru[f->unit] - HEADERLEN) + datalen = peer_mru[f->unit] - HEADERLEN; + if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) + BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen); + outlen = datalen + HEADERLEN; + MAKEHEADER(outp, f->protocol); + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + output(f->unit, outpacket_buf, outlen + PPP_HDRLEN); + + FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d, id %d.", + PROTO_NAME(f), code, id)); +} diff --git a/cpukit/pppd/fsm.h b/cpukit/pppd/fsm.h new file mode 100644 index 0000000000..f289429fdc --- /dev/null +++ b/cpukit/pppd/fsm.h @@ -0,0 +1,144 @@ +/* + * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * Packet header = Code, id, length. + */ +#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * CP (LCP, IPCP, etc.) codes. + */ +#define CONFREQ 1 /* Configuration Request */ +#define CONFACK 2 /* Configuration Ack */ +#define CONFNAK 3 /* Configuration Nak */ +#define CONFREJ 4 /* Configuration Reject */ +#define TERMREQ 5 /* Termination Request */ +#define TERMACK 6 /* Termination Ack */ +#define CODEREJ 7 /* Code Reject */ + + +/* + * Each FSM is described by an fsm structure and fsm callbacks. + */ +typedef struct fsm { + int unit; /* Interface unit number */ + int protocol; /* Data Link Layer Protocol field value */ + int state; /* State */ + int flags; /* Contains option bits */ + u_char id; /* Current id */ + u_char reqid; /* Current request id */ + u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */ + int timeouttime; /* Timeout time in milliseconds */ + int maxconfreqtransmits; /* Maximum Configure-Request transmissions */ + int retransmits; /* Number of retransmissions left */ + int maxtermtransmits; /* Maximum Terminate-Request transmissions */ + int nakloops; /* Number of nak loops since last ack */ + int maxnakloops; /* Maximum number of nak loops tolerated */ + struct fsm_callbacks *callbacks; /* Callback routines */ + char *term_reason; /* Reason for closing protocol */ + int term_reason_len; /* Length of term_reason */ +} fsm; + + +typedef struct fsm_callbacks { + void (*resetci) /* Reset our Configuration Information */ + __P((fsm *)); + int (*cilen) /* Length of our Configuration Information */ + __P((fsm *)); + void (*addci) /* Add our Configuration Information */ + __P((fsm *, u_char *, int *)); + int (*ackci) /* ACK our Configuration Information */ + __P((fsm *, u_char *, int)); + int (*nakci) /* NAK our Configuration Information */ + __P((fsm *, u_char *, int)); + int (*rejci) /* Reject our Configuration Information */ + __P((fsm *, u_char *, int)); + int (*reqci) /* Request peer's Configuration Information */ + __P((fsm *, u_char *, int *, int)); + void (*up) /* Called when fsm reaches OPENED state */ + __P((fsm *)); + void (*down) /* Called when fsm leaves OPENED state */ + __P((fsm *)); + void (*starting) /* Called when we want the lower layer */ + __P((fsm *)); + void (*finished) /* Called when we don't want the lower layer */ + __P((fsm *)); + void (*protreject) /* Called when Protocol-Reject received */ + __P((int)); + void (*retransmit) /* Retransmission is necessary */ + __P((fsm *)); + int (*extcode) /* Called when unknown code received */ + __P((fsm *, int, int, u_char *, int)); + char *proto_name; /* String name for protocol (for messages) */ +} fsm_callbacks; + + +/* + * Link states. + */ +#define INITIAL 0 /* Down, hasn't been opened */ +#define STARTING 1 /* Down, been opened */ +#define CLOSED 2 /* Up, hasn't been opened */ +#define STOPPED 3 /* Open, waiting for down event */ +#define CLOSING 4 /* Terminating the connection, not open */ +#define STOPPING 5 /* Terminating, but open */ +#define REQSENT 6 /* We've sent a Config Request */ +#define ACKRCVD 7 /* We've received a Config Ack */ +#define ACKSENT 8 /* We've sent a Config Ack */ +#define OPENED 9 /* Connection available */ + + +/* + * Flags - indicate options controlling FSM operation + */ +#define OPT_PASSIVE 1 /* Don't die if we don't get a response */ +#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */ +#define OPT_SILENT 4 /* Wait for peer to speak first */ + + +/* + * Timeouts. + */ +#define DEFTIMEOUT 3 /* Timeout time in seconds */ +#define DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#define DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ +#define DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ + + +/* + * Prototypes + */ +void fsm_init __P((fsm *)); +void fsm_lowerup __P((fsm *)); +void fsm_lowerdown __P((fsm *)); +void fsm_open __P((fsm *)); +void fsm_close __P((fsm *, char *)); +void fsm_input __P((fsm *, u_char *, int)); +void fsm_protreject __P((fsm *)); +void fsm_sdata __P((fsm *, int, int, u_char *, int)); + + +/* + * Variables + */ +extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */ diff --git a/cpukit/pppd/ipcp.c b/cpukit/pppd/ipcp.c new file mode 100644 index 0000000000..5553d3194d --- /dev/null +++ b/cpukit/pppd/ipcp.c @@ -0,0 +1,1506 @@ +/* + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +/* static char rcsid[] = "$Id$"; */ +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +/* #include */ + +#include "pppd.h" +#include "fsm.h" +#include "ipcp.h" +#include "pathnames.h" + +/* global vars */ +ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ + +/* local vars */ +static int cis_received[NUM_PPP]; /* # Conf-Reqs received */ +static int default_route_set[NUM_PPP]; /* Have set up a default route */ +static int proxy_arp_set[NUM_PPP]; /* Have created proxy arp entry */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void ipcp_resetci __P((fsm *)); /* Reset our CI */ +static int ipcp_cilen __P((fsm *)); /* Return length of our CI */ +static void ipcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */ +static int ipcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */ +static int ipcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */ +static int ipcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */ +static int ipcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */ +static void ipcp_up __P((fsm *)); /* We're UP */ +static void ipcp_down __P((fsm *)); /* We're DOWN */ +#if 0 +static void ipcp_script __P((fsm *, char *)); /* Run an up/down script */ +#endif +static void ipcp_finished __P((fsm *)); /* Don't need lower layer */ + +fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */ + +static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ + ipcp_resetci, /* Reset our Configuration Information */ + ipcp_cilen, /* Length of our Configuration Information */ + ipcp_addci, /* Add our Configuration Information */ + ipcp_ackci, /* ACK our Configuration Information */ + ipcp_nakci, /* NAK our Configuration Information */ + ipcp_rejci, /* Reject our Configuration Information */ + ipcp_reqci, /* Request peer's Configuration Information */ + ipcp_up, /* Called when fsm reaches OPENED state */ + ipcp_down, /* Called when fsm leaves OPENED state */ + NULL, /* Called when we want the lower layer up */ + ipcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPCP" /* String name of protocol */ +}; + +/* + * Protocol entry points from main code. + */ +static void ipcp_init __P((int)); +static void ipcp_open __P((int)); +static void ipcp_close __P((int, char *)); +static void ipcp_lowerup __P((int)); +static void ipcp_lowerdown __P((int)); +static void ipcp_input __P((int, u_char *, int)); +static void ipcp_protrej __P((int)); +static int ipcp_printpkt __P((u_char *, int, + void (*) __P((void *, char *, ...)), void *)); +static void ip_check_options __P((void)); +static int ip_demand_conf __P((int)); +static int ip_active_pkt __P((u_char *, int)); + +struct protent ipcp_protent = { + PPP_IPCP, + ipcp_init, + ipcp_input, + ipcp_protrej, + ipcp_lowerup, + ipcp_lowerdown, + ipcp_open, + ipcp_close, + ipcp_printpkt, + NULL, + 1, + "IPCP", + ip_check_options, + ip_demand_conf, + ip_active_pkt +}; + +static void ipcp_clear_addrs __P((int)); + +/* + * Lengths of configuration options. + */ +#define CILEN_VOID 2 +#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */ +#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */ +#define CILEN_ADDR 6 /* new-style single address option */ +#define CILEN_ADDRS 10 /* old-style dual address option */ + + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + + +/* + * Make a string representation of a network IP address. + */ +char * +ip_ntoa(ipaddr) +u_int32_t ipaddr; +{ + static char b[64]; + + ipaddr = ntohl(ipaddr); + + sprintf(b, "%d.%d.%d.%d", + (u_char)(ipaddr >> 24), + (u_char)(ipaddr >> 16), + (u_char)(ipaddr >> 8), + (u_char)(ipaddr)); + return b; +} + + +/* + * ipcp_init - Initialize IPCP. + */ +static void +ipcp_init(unit) + int unit; +{ + fsm *f = &ipcp_fsm[unit]; + ipcp_options *wo = &ipcp_wantoptions[unit]; + ipcp_options *ao = &ipcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_IPCP; + f->callbacks = &ipcp_callbacks; + fsm_init(&ipcp_fsm[unit]); + + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); + + wo->neg_addr = 1; + wo->neg_vj = 1; + wo->vj_protocol = IPCP_VJ_COMP; + wo->maxslotindex = MAX_STATES - 1; /* really max index */ + wo->cflag = 1; + ipcp_wantoptions[0].default_route = 1; + /* max slots and slot-id compression are currently hardwired in */ + /* ppp_if.c to 16 and 1, this needs to be changed (among other */ + /* things) gmc */ + + ao->neg_addr = 1; + ao->neg_vj = 1; + ao->maxslotindex = MAX_STATES - 1; + ao->cflag = 1; + + /* + * XXX These control whether the user may use the proxyarp + * and defaultroute options. + */ + ao->proxy_arp = 1; + ao->default_route = 1; +} + + +/* + * ipcp_open - IPCP is allowed to come up. + */ +static void +ipcp_open(unit) + int unit; +{ + fsm_open(&ipcp_fsm[unit]); +} + + +/* + * ipcp_close - Take IPCP down. + */ +static void +ipcp_close(unit, reason) + int unit; + char *reason; +{ + fsm_close(&ipcp_fsm[unit], reason); +} + + +/* + * ipcp_lowerup - The lower layer is up. + */ +static void +ipcp_lowerup(unit) + int unit; +{ + fsm_lowerup(&ipcp_fsm[unit]); +} + + +/* + * ipcp_lowerdown - The lower layer is down. + */ +static void +ipcp_lowerdown(unit) + int unit; +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_input - Input IPCP packet. + */ +static void +ipcp_input(unit, p, len) + int unit; + u_char *p; + int len; +{ + fsm_input(&ipcp_fsm[unit], p, len); +} + + +/* + * ipcp_protrej - A Protocol-Reject was received for IPCP. + * + * Pretend the lower layer went down, so we shut up. + */ +static void +ipcp_protrej(unit) + int unit; +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_resetci - Reset our CI. + */ +static void +ipcp_resetci(f) + fsm *f; +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr; + if (wo->ouraddr == 0) + wo->accept_local = 1; + if (wo->hisaddr == 0) + wo->accept_remote = 1; + ipcp_gotoptions[f->unit] = *wo; + cis_received[f->unit] = 0; +} + + +/* + * ipcp_cilen - Return length of our CI. + */ +static int +ipcp_cilen(f) + fsm *f; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + +#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) +#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0) + + /* + * First see if we want to change our options to the old + * forms because we have received old forms from the peer. + */ + if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { + /* use the old style of address negotiation */ + go->neg_addr = 1; + go->old_addrs = 1; + } + if (wo->neg_vj && !go->neg_vj && !go->old_vj) { + /* try an older style of VJ negotiation */ + if (cis_received[f->unit] == 0) { + /* keep trying the new style until we see some CI from the peer */ + go->neg_vj = 1; + } else { + /* use the old style only if the peer did */ + if (ho->neg_vj && ho->old_vj) { + go->neg_vj = 1; + go->old_vj = 1; + go->vj_protocol = ho->vj_protocol; + } + } + } + + return (LENCIADDR(go->neg_addr, go->old_addrs) + + LENCIVJ(go->neg_vj, go->old_vj)); +} + + +/* + * ipcp_addci - Add our desired CIs to a packet. + */ +static void +ipcp_addci(f, ucp, lenp) + fsm *f; + u_char *ucp; + int *lenp; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + int len = *lenp; + +#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + if (!old) { \ + PUTCHAR(maxslotindex, ucp); \ + PUTCHAR(cflag, ucp); \ + } \ + len -= vjlen; \ + } else \ + neg = 0; \ + } + +#define ADDCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + if (len >= addrlen) { \ + u_int32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(addrlen, ucp); \ + l = ntohl(val1); \ + PUTLONG(l, ucp); \ + if (old) { \ + l = ntohl(val2); \ + PUTLONG(l, ucp); \ + } \ + len -= addrlen; \ + } else \ + neg = 0; \ + } + + ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + *lenp -= len; +} + + +/* + * ipcp_ackci - Ack our CIs. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +ipcp_ackci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_short cilen, citype, cishort; + u_int32_t cilong; + u_char cimaxslotindex, cicflag; + + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + +#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if ((len -= vjlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslotindex) \ + goto bad; \ + GETCHAR(cicflag, p); \ + if (cicflag != cflag) \ + goto bad; \ + } \ + } + +#define ACKCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + u_int32_t l; \ + if ((len -= addrlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != addrlen || \ + citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val1 != cilong) \ + goto bad; \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val2 != cilong) \ + goto bad; \ + } \ + } + + ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!")); + return (0); +} + +/* + * ipcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if IPCP is in the OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +ipcp_nakci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, cicflag; + u_char citype, cilen, *next; + u_short cishort; + u_int32_t ciaddr1, ciaddr2, l; + ipcp_options no; /* options we've seen Naks for */ + ipcp_options try; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIADDR(opt, neg, old, code) \ + if (go->neg && \ + len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + if (old) { \ + GETLONG(l, p); \ + ciaddr2 = htonl(l); \ + no.old_addrs = 1; \ + } else \ + ciaddr2 = 0; \ + no.neg = 1; \ + code \ + } + +#define NAKCIVJ(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } + + /* + * Accept the peer's idea of {our,his} address, if different + * from our idea, only if the accept_{local,remote} flag is set. + */ + NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs, + if (go->accept_local && ciaddr1) { /* Do we know our address? */ + try.ouraddr = ciaddr1; + IPCPDEBUG((LOG_INFO, "local IP address %s", + ip_ntoa(ciaddr1))); + } + if (go->accept_remote && ciaddr2) { /* Does he know his? */ + try.hisaddr = ciaddr2; + IPCPDEBUG((LOG_INFO, "remote IP address %s", + ip_ntoa(ciaddr2))); + } + ); + + /* + * Accept the peer's value of maxslotindex provided that it + * is less than what we asked for. Turn off slot-ID compression + * if the peer wants. Send old-style compress-type option if + * the peer wants. + */ + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + if (cilen == CILEN_VJ) { + GETCHAR(cimaxslotindex, p); + GETCHAR(cicflag, p); + if (cishort == IPCP_VJ_COMP) { + try.old_vj = 0; + if (cimaxslotindex < go->maxslotindex) + try.maxslotindex = cimaxslotindex; + if (!cicflag) + try.cflag = 0; + } else { + try.neg_vj = 0; + } + } else { + if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) { + try.old_vj = 1; + try.vj_protocol = cishort; + } else { + try.neg_vj = 0; + } + } + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If they want to negotiate about IP addresses, we comply. + * If they want us to ask for compression, we refuse. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if( (len -= cilen) < 0 ) + goto bad; + next = p + cilen - 2; + + switch (citype) { + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) + goto bad; + no.neg_vj = 1; + break; + case CI_ADDRS: + if ((go->neg_addr && go->old_addrs) || no.old_addrs + || cilen != CILEN_ADDRS) + goto bad; + try.neg_addr = 1; + try.old_addrs = 1; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) + try.ouraddr = ciaddr1; + GETLONG(l, p); + ciaddr2 = htonl(l); + if (ciaddr2 && go->accept_remote) + try.hisaddr = ciaddr2; + no.old_addrs = 1; + break; + case CI_ADDR: + if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) + goto bad; + try.old_addrs = 0; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) + try.ouraddr = ciaddr1; + if (try.ouraddr != 0) + try.neg_addr = 1; + no.neg_addr = 1; + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) + goto bad; + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != OPENED) + *go = try; + + return 1; + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_nakci: received bad Nak!")); + return 0; +} + + +/* + * ipcp_rejci - Reject some of our CIs. + */ +static int +ipcp_rejci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, ciflag, cilen; + u_short cishort; + u_int32_t cilong; + ipcp_options try; /* options to request next time */ + + try = *go; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIADDR(opt, neg, old, val1, val2) \ + if (go->neg && \ + len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \ + p[1] == cilen && \ + p[0] == opt) { \ + u_int32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val1) \ + goto bad; \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val2) \ + goto bad; \ + } \ + try.neg = 0; \ + } + +#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \ + if (go->neg && \ + p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslot) \ + goto bad; \ + GETCHAR(ciflag, p); \ + if (ciflag != cflag) \ + goto bad; \ + } \ + try.neg = 0; \ + } + + REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + /* + * Now we can update state. + */ + if (f->state != OPENED) + *go = try; + return 1; + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!")); + return 0; +} + + +/* + * ipcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +ipcp_reqci(f, inp, len, reject_if_disagree) + fsm *f; + u_char *inp; /* Requested CIs */ + int *len; /* Length of requested CIs */ + int reject_if_disagree; +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *ao = &ipcp_allowoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ + u_short cishort; /* Parsed short value */ + u_int32_t tl, ciaddr1, ciaddr2;/* Parsed address values */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + u_char maxslotindex, cflag; + int d; + + cis_received[f->unit] = 1; + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_ADDRS: + IPCPDEBUG((LOG_INFO, "ipcp: received ADDRS ")); + if (!ao->neg_addr || + cilen != CILEN_ADDRS) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + IPCPDEBUG((LOG_INFO, "(%s:", ip_ntoa(ciaddr1))); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u_int32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * If neither we nor he knows his address, reject the option. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + /* + * If he doesn't know our address, or if we both have our address + * but disagree about it, then NAK it with our idea. + */ + GETLONG(tl, p); /* Parse desination address (ours) */ + ciaddr2 = htonl(tl); + IPCPDEBUG((LOG_INFO, "%s)", ip_ntoa(ciaddr2))); + if (ciaddr2 != wo->ouraddr) { + if (ciaddr2 == 0 || !wo->accept_local) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u_int32_t), p); + tl = ntohl(wo->ouraddr); + PUTLONG(tl, p); + } + } else { + go->ouraddr = ciaddr2; /* accept peer's idea */ + } + } + + ho->neg_addr = 1; + ho->old_addrs = 1; + ho->hisaddr = ciaddr1; + ho->ouraddr = ciaddr2; + break; + + case CI_ADDR: + IPCPDEBUG((LOG_INFO, "ipcp: received ADDR ")); + + if (!ao->neg_addr || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + IPCPDEBUG((LOG_INFO, "(%s)", ip_ntoa(ciaddr1))); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u_int32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * Don't ACK an address of 0.0.0.0 - reject it instead. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + ho->neg_addr = 1; + ho->hisaddr = ciaddr1; + break; + + case CI_MS_DNS1: + case CI_MS_DNS2: + /* Microsoft primary or secondary DNS request */ + d = citype == CI_MS_DNS2; + IPCPDEBUG((LOG_INFO, "ipcp: received DNS%d Request ", d+1)); + + /* If we do not have a DNS address then we cannot send it */ + if (ao->dnsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->dnsaddr[d]) { + DECPTR(sizeof(u_int32_t), p); + tl = ntohl(ao->dnsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; + + case CI_MS_WINS1: + case CI_MS_WINS2: + /* Microsoft primary or secondary WINS request */ + d = citype == CI_MS_WINS2; + IPCPDEBUG((LOG_INFO, "ipcp: received WINS%d Request ", d+1)); + + /* If we do not have a DNS address then we cannot send it */ + if (ao->winsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->winsaddr[d]) { + DECPTR(sizeof(u_int32_t), p); + tl = ntohl(ao->winsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; + + case CI_COMPRESSTYPE: + IPCPDEBUG((LOG_INFO, "ipcp: received COMPRESSTYPE ")); + if (!ao->neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) { + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + IPCPDEBUG((LOG_INFO, "(%d)", cishort)); + + if (!(cishort == IPCP_VJ_COMP || + (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) { + orc = CONFREJ; + break; + } + + ho->neg_vj = 1; + ho->vj_protocol = cishort; + if (cilen == CILEN_VJ) { + GETCHAR(maxslotindex, p); + if (maxslotindex > ao->maxslotindex) { + orc = CONFNAK; + if (!reject_if_disagree){ + DECPTR(1, p); + PUTCHAR(ao->maxslotindex, p); + } + } + GETCHAR(cflag, p); + if (cflag && !ao->cflag) { + orc = CONFNAK; + if (!reject_if_disagree){ + DECPTR(1, p); + PUTCHAR(wo->cflag, p); + } + } + ho->maxslotindex = maxslotindex; + ho->cflag = cflag; + } else { + ho->old_vj = 1; + ho->maxslotindex = MAX_STATES - 1; + ho->cflag = 1; + } + break; + + default: + orc = CONFREJ; + break; + } + +endswitch: + IPCPDEBUG((LOG_INFO, " (%s)\n", CODENAME(orc))); + + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) /* Getting fed up with sending NAKs? */ + orc = CONFREJ; /* Get tough if so */ + else { + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) + BCOPY(cip, ucp, cilen); /* Move it */ + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their address, and they didn't send their address, then we + * send a NAK with a CI_ADDR option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + if (rc != CONFREJ && !ho->neg_addr && + wo->req_addr && !reject_if_disagree) { + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_addr = 0; /* don't ask again */ + } + PUTCHAR(CI_ADDR, ucp); + PUTCHAR(CILEN_ADDR, ucp); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, ucp); + } + + *len = ucp - inp; /* Compute output length */ + IPCPDEBUG((LOG_INFO, "ipcp: returning Configure-%s", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +/* + * ip_check_options - check that any IP-related options are OK, + * and assign appropriate defaults. + */ +static void +ip_check_options() +{ + struct hostent *hp; + u_int32_t local; + ipcp_options *wo = &ipcp_wantoptions[0]; + + /* + * Default our local IP address based on our hostname. + * If local IP address already given, don't bother. + */ + if (wo->ouraddr == 0 && !disable_defaultip) { + /* + * Look up our hostname (possibly with domain name appended) + * and take the first IP address as our local IP address. + * If there isn't an IP address for our hostname, too bad. + */ + wo->accept_local = 1; /* don't insist on this default value */ + if ((hp = gethostbyname(hostname)) != NULL) { + local = *(u_int32_t *)hp->h_addr; + if (local != 0 && !bad_ip_adrs(local)) + wo->ouraddr = local; + } + } + +} + + +/* + * ip_demand_conf - configure the interface as though + * IPCP were up, for use with dial-on-demand. + */ +static int +ip_demand_conf(u) + int u; +{ + ipcp_options *wo = &ipcp_wantoptions[u]; + + if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr))) + return 0; + if (!sifup(u)) + return 0; + if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE)) + return 0; + if (wo->default_route) + if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr)) + default_route_set[u] = 1; + if (wo->proxy_arp) + if (sifproxyarp(u, wo->hisaddr)) + proxy_arp_set[u] = 1; + + syslog(LOG_NOTICE, "local IP address %s", ip_ntoa(wo->ouraddr)); + syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(wo->hisaddr)); + + return 1; +} + + +/* + * ipcp_up - IPCP has come UP. + * + * Configure the IP network interface appropriately and bring it up. + */ +#define script_setenv(a,b) + + +static void +ipcp_up(f) + fsm *f; +{ + u_int32_t mask; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + np_up(f->unit, PPP_IP); + IPCPDEBUG((LOG_INFO, "ipcp: up")); + + /* + * We must have a non-zero IP address for both ends of the link. + */ + if (!ho->neg_addr) + ho->hisaddr = wo->hisaddr; + + if (ho->hisaddr == 0) { + syslog(LOG_ERR, "Could not determine remote IP address"); + ipcp_close(f->unit, "Could not determine remote IP address"); + return; + } + if (go->ouraddr == 0) { + syslog(LOG_ERR, "Could not determine local IP address"); + ipcp_close(f->unit, "Could not determine local IP address"); + return; + } +/* script_setenv("IPLOCAL", ip_ntoa(go->ouraddr)); + script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr)); +*/ + /* + * Check that the peer is allowed to use the IP address it wants. + */ + if (!auth_ip_addr(f->unit, ho->hisaddr)) { + syslog(LOG_ERR, "Peer is not authorized to use remote address %s", + ip_ntoa(ho->hisaddr)); + ipcp_close(f->unit, "Unauthorized remote IP address"); + return; + } + + /* set tcp compression */ + sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex); + + /* + * If we are doing dial-on-demand, the interface is already + * configured, so we put out any saved-up packets, then set the + * interface to pass IP packets. + */ + { + /* + * Set IP addresses and (if specified) netmask. + */ + mask = GetMask(go->ouraddr); + +#if !(defined(SVR4) && (defined(SNI) || defined(__USLC__))) + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) { + IPCPDEBUG((LOG_WARNING, "sifaddr failed")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } +#endif + + /* bring the interface up for IP */ + if (!sifup(f->unit)) { + IPCPDEBUG((LOG_WARNING, "sifup failed")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + +#if (defined(SVR4) && (defined(SNI) || defined(__USLC__))) + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) { + IPCPDEBUG((LOG_WARNING, "sifaddr failed")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } +#endif + sifnpmode(f->unit, PPP_IP, NPMODE_PASS); + + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) + if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) + default_route_set[f->unit] = 1; + + /* Make a proxy ARP entry if requested. */ + if (ipcp_wantoptions[f->unit].proxy_arp) + if (sifproxyarp(f->unit, ho->hisaddr)) + proxy_arp_set[f->unit] = 1; + + syslog(LOG_NOTICE, "local IP address %s", ip_ntoa(go->ouraddr)); + syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(ho->hisaddr)); + } + + /* + * Execute the ip-up script, like this: + * /etc/ppp/ip-up interface tty speed local-IP remote-IP + */ + { +#if 0 +/* XXX PPPConfiguration */ + GlobalSystemStatus *stat; + stat=LockSTBSystemParam(); + stat->ConnectionStatus=Connected; + UnlockSTBSystemParam(); +#endif + } +} + + +/* + * ipcp_down - IPCP has gone DOWN. + * + * Take the IP network interface down, clear its addresses + * and delete routes through it. + */ +static void +ipcp_down(f) + fsm *f; +{ + IPCPDEBUG((LOG_INFO, "ipcp: down")); + np_down(f->unit, PPP_IP); + sifvjcomp(f->unit, 0, 0, 0); + + /* + * If we are doing dial-on-demand, set the interface + * to queue up outgoing packets (for now). + */ + sifdown(f->unit); + ipcp_clear_addrs(f->unit); + + /* Execute the ip-down script */ + { +#if 0 +/* XXX PPPConfiguration */ + GlobalSystemStatus *stat; + stat=LockSTBSystemParam(); + stat->ConnectionStatus=NotConnected; + UnlockSTBSystemParam(); +#endif + } +} + + +/* + * ipcp_clear_addrs() - clear the interface addresses, routes, + * proxy arp entries, etc. + */ +static void +ipcp_clear_addrs(unit) + int unit; +{ + u_int32_t ouraddr, hisaddr; + + ouraddr = ipcp_gotoptions[unit].ouraddr; + hisaddr = ipcp_hisoptions[unit].hisaddr; + if (proxy_arp_set[unit]) { + cifproxyarp(unit, hisaddr); + proxy_arp_set[unit] = 0; + } + if (default_route_set[unit]) { + cifdefaultroute(unit, ouraddr, hisaddr); + default_route_set[unit] = 0; + } + cifaddr(unit, ouraddr, hisaddr); +} + + +/* + * ipcp_finished - possibly shut down the lower layers. + */ +static void +ipcp_finished(f) + fsm *f; +{ + np_finished(f->unit, PPP_IP); +} + + +#if 0 +/* + * ipcp_script - Execute a script with arguments + * interface-name tty-name speed local-IP remote-IP. + */ +static void +ipcp_script(f, script) + fsm *f; + char *script; +{ + char strspeed[32], strlocal[32], strremote[32]; + char *argv[8]; + + sprintf(strspeed, "%d", baud_rate); + strcpy(strlocal, ip_ntoa(ipcp_gotoptions[f->unit].ouraddr)); + strcpy(strremote, ip_ntoa(ipcp_hisoptions[f->unit].hisaddr)); + + argv[0] = script; + argv[1] = ifname; + argv[2] = devnam; + argv[3] = strspeed; + argv[4] = strlocal; + argv[5] = strremote; + argv[6] = ipparam; + argv[7] = NULL; + run_program(script, argv, 0); +} +#endif + +/* + * ipcp_printpkt - print the contents of an IPCP packet. + */ +static char *ipcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej" +}; + +static int +ipcp_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int code, id, len, olen; + u_char *pstart, *optend; + u_short cishort; + u_int32_t cilong; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(ipcp_codenames) / sizeof(char *)) + printer(arg, " %s", ipcp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_ADDRS: + if (olen == CILEN_ADDRS) { + p += 2; + GETLONG(cilong, p); + printer(arg, "addrs %I", htonl(cilong)); + GETLONG(cilong, p); + printer(arg, " %I", htonl(cilong)); + } + break; + case CI_COMPRESSTYPE: + if (olen >= CILEN_COMPRESS) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "compress "); + switch (cishort) { + case IPCP_VJ_COMP: + printer(arg, "VJ"); + break; + case IPCP_VJ_COMP_OLD: + printer(arg, "old-VJ"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_ADDR: + if (olen == CILEN_ADDR) { + p += 2; + GETLONG(cilong, p); + printer(arg, "addr %I", htonl(cilong)); + } + break; + case CI_MS_DNS1: + case CI_MS_DNS2: + p += 2; + GETLONG(cilong, p); + printer(arg, "ms-dns %I", htonl(cilong)); + break; + case CI_MS_WINS1: + case CI_MS_WINS2: + p += 2; + GETLONG(cilong, p); + printer(arg, "ms-wins %I", htonl(cilong)); + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + print_string(p, len, printer, arg); + p += len; + len = 0; + } + break; + } + + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} + +/* + * ip_active_pkt - see if this IP packet is worth bringing the link up for. + * We don't bring the link up for IP fragments or for TCP FIN packets + * with no data. + */ +#define IP_HDRLEN 20 /* bytes */ +#define IP_OFFMASK 0x1fff +#define IPPROTO_TCP 6 +#define TCP_HDRLEN 20 +#define TH_FIN 0x01 + +/* + * We use these macros because the IP header may be at an odd address, + * and some compilers might use word loads to get th_off or ip_hl. + */ + +#define net_short(x) (((x)[0] << 8) + (x)[1]) +#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF) +#define get_ipoff(x) net_short((unsigned char *)(x) + 6) +#define get_ipproto(x) (((unsigned char *)(x))[9]) +#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) +#define get_tcpflags(x) (((unsigned char *)(x))[13]) + +static int +ip_active_pkt(pkt, len) + u_char *pkt; + int len; +{ + u_char *tcp; + int hlen; + + len -= PPP_HDRLEN; + pkt += PPP_HDRLEN; + if (len < IP_HDRLEN) + return 0; + if ((get_ipoff(pkt) & IP_OFFMASK) != 0) + return 0; + if (get_ipproto(pkt) != IPPROTO_TCP) + return 1; + hlen = get_iphl(pkt) * 4; + if (len < hlen + TCP_HDRLEN) + return 0; + tcp = pkt + hlen; + if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) + return 0; + return 1; +} diff --git a/cpukit/pppd/ipcp.h b/cpukit/pppd/ipcp.h new file mode 100644 index 0000000000..e2c7eb44db --- /dev/null +++ b/cpukit/pppd/ipcp.h @@ -0,0 +1,70 @@ +/* + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * Options. + */ +#define CI_ADDRS 1 /* IP Addresses */ +#define CI_COMPRESSTYPE 2 /* Compression Type */ +#define CI_ADDR 3 + +#define CI_MS_DNS1 129 /* Primary DNS value */ +#define CI_MS_WINS1 130 /* Primary WINS value */ +#define CI_MS_DNS2 131 /* Secondary DNS value */ +#define CI_MS_WINS2 132 /* Secondary WINS value */ + +#define MAX_STATES 16 /* from slcompress.h */ + +#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */ +#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */ +#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */ + /* maxslot and slot number compression) */ + +#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option*/ +#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */ + /* compression option*/ + +typedef struct ipcp_options { + int neg_addr : 1; /* Negotiate IP Address? */ + int old_addrs : 1; /* Use old (IP-Addresses) option? */ + int req_addr : 1; /* Ask peer to send IP address? */ + int default_route : 1; /* Assign default route through interface? */ + int proxy_arp : 1; /* Make proxy ARP entry for peer? */ + int neg_vj : 1; /* Van Jacobson Compression? */ + int old_vj : 1; /* use old (short) form of VJ option? */ + int accept_local : 1; /* accept peer's value for ouraddr */ + int accept_remote : 1; /* accept peer's value for hisaddr */ + u_short vj_protocol; /* protocol value to use in VJ option */ + u_char maxslotindex, cflag; /* values for RFC1332 VJ compression neg. */ + u_int32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */ + u_int32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */ + u_int32_t winsaddr[2]; /* Primary and secondary MS WINS entries */ +} ipcp_options; + +extern fsm ipcp_fsm[]; +extern ipcp_options ipcp_wantoptions[]; +extern ipcp_options ipcp_gotoptions[]; +extern ipcp_options ipcp_allowoptions[]; +extern ipcp_options ipcp_hisoptions[]; + +char *ip_ntoa __P((u_int32_t)); + +extern struct protent ipcp_protent; diff --git a/cpukit/pppd/ipxcp.c b/cpukit/pppd/ipxcp.c new file mode 100644 index 0000000000..be85f31231 --- /dev/null +++ b/cpukit/pppd/ipxcp.c @@ -0,0 +1,1399 @@ +/* + * ipxcp.c - PPP IPX Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifdef IPX_CHANGE +#ifndef lint +/* static char rcsid[] = "$Id$"; */ +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include +#include +#include + +#include "pppd.h" +#include "fsm.h" +#include "ipxcp.h" +#include "pathnames.h" + +/* global vars */ +ipxcp_options ipxcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +ipxcp_options ipxcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +ipxcp_options ipxcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +ipxcp_options ipxcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ + +#define wo (&ipxcp_wantoptions[0]) +#define ao (&ipxcp_allowoptions[0]) +#define go (&ipxcp_gotoptions[0]) +#define ho (&ipxcp_hisoptions[0]) + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void ipxcp_resetci __P((fsm *)); /* Reset our CI */ +static int ipxcp_cilen __P((fsm *)); /* Return length of our CI */ +static void ipxcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */ +static int ipxcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */ +static int ipxcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */ +static int ipxcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */ +static int ipxcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */ +static void ipxcp_up __P((fsm *)); /* We're UP */ +static void ipxcp_down __P((fsm *)); /* We're DOWN */ +static void ipxcp_script __P((fsm *, char *)); /* Run an up/down script */ + +fsm ipxcp_fsm[NUM_PPP]; /* IPXCP fsm structure */ + +static fsm_callbacks ipxcp_callbacks = { /* IPXCP callback routines */ + ipxcp_resetci, /* Reset our Configuration Information */ + ipxcp_cilen, /* Length of our Configuration Information */ + ipxcp_addci, /* Add our Configuration Information */ + ipxcp_ackci, /* ACK our Configuration Information */ + ipxcp_nakci, /* NAK our Configuration Information */ + ipxcp_rejci, /* Reject our Configuration Information */ + ipxcp_reqci, /* Request peer's Configuration Information */ + ipxcp_up, /* Called when fsm reaches OPENED state */ + ipxcp_down, /* Called when fsm leaves OPENED state */ + NULL, /* Called when we want the lower layer up */ + NULL, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPXCP" /* String name of protocol */ +}; + +/* + * Protocol entry points. + */ + +static void ipxcp_init __P((int)); +static void ipxcp_open __P((int)); +static void ipxcp_close __P((int, char *)); +static void ipxcp_lowerup __P((int)); +static void ipxcp_lowerdown __P((int)); +static void ipxcp_input __P((int, u_char *, int)); +static void ipxcp_protrej __P((int)); +static int ipxcp_printpkt __P((u_char *, int, + void (*) __P((void *, char *, ...)), void *)); + +struct protent ipxcp_protent = { + PPP_IPXCP, + ipxcp_init, + ipxcp_input, + ipxcp_protrej, + ipxcp_lowerup, + ipxcp_lowerdown, + ipxcp_open, + ipxcp_close, + ipxcp_printpkt, + NULL, + 0, + "IPXCP", + NULL, + NULL, + NULL +}; + +/* + * Lengths of configuration options. + */ + +#define CILEN_VOID 2 +#define CILEN_COMPLETE 2 /* length of complete option */ +#define CILEN_NETN 6 /* network number length option */ +#define CILEN_NODEN 8 /* node number length option */ +#define CILEN_PROTOCOL 4 /* Minimum length of routing protocol */ +#define CILEN_NAME 3 /* Minimum length of router name */ +#define CILEN_COMPRESS 4 /* Minimum length of compression protocol */ + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + +/* Used in printing the node number */ +#define NODE(base) base[0], base[1], base[2], base[3], base[4], base[5] + +/* Used to generate the proper bit mask */ +#define BIT(num) (1 << (num)) + +/* + * Convert from internal to external notation + */ + +static short int +to_external(internal) +short int internal; +{ + short int external; + + if (internal & IPX_NONE) + external = IPX_NONE; + else + external = RIP_SAP; + + return external; +} + +/* + * Make a string representation of a network IP address. + */ + +char * +ipx_ntoa(ipxaddr) +u_int32_t ipxaddr; +{ + static char b[64]; + sprintf(b, "%x", ipxaddr); + return b; +} + + +/* + * ipxcp_init - Initialize IPXCP. + */ +static void +ipxcp_init(unit) + int unit; +{ + fsm *f = &ipxcp_fsm[unit]; + + f->unit = unit; + f->protocol = PPP_IPXCP; + f->callbacks = &ipxcp_callbacks; + fsm_init(&ipxcp_fsm[unit]); + + memset (wo->name, 0, sizeof (wo->name)); + memset (wo->our_node, 0, sizeof (wo->our_node)); + memset (wo->his_node, 0, sizeof (wo->his_node)); + + wo->neg_nn = 1; + wo->neg_complete = 1; + wo->network = 0; + + ao->neg_node = 1; + ao->neg_nn = 1; + ao->neg_name = 1; + ao->neg_complete = 1; + ao->neg_router = 1; + + ao->accept_local = 0; + ao->accept_remote = 0; + ao->accept_network = 0; + + wo->tried_rip = 0; + wo->tried_nlsp = 0; +} + +/* + * Copy the node number + */ + +static void +copy_node (src, dst) +u_char *src, *dst; +{ + memcpy (dst, src, sizeof (ipxcp_wantoptions[0].our_node)); +} + +/* + * Compare node numbers + */ + +static int +compare_node (src, dst) +u_char *src, *dst; +{ + return memcmp (dst, src, sizeof (ipxcp_wantoptions[0].our_node)) == 0; +} + +/* + * Is the node number zero? + */ + +static int +zero_node (node) +u_char *node; +{ + int indx; + for (indx = 0; indx < sizeof (ipxcp_wantoptions[0].our_node); ++indx) + if (node [indx] != 0) + return 0; + return 1; +} + +/* + * Increment the node number + */ + +static void +inc_node (node) +u_char *node; +{ + u_char *outp; + u_int32_t magic_num; + + outp = node; + magic_num = magic(); + *outp++ = '\0'; + *outp++ = '\0'; + PUTLONG (magic_num, outp); +} + +/* + * ipxcp_open - IPXCP is allowed to come up. + */ +static void +ipxcp_open(unit) + int unit; +{ + fsm_open(&ipxcp_fsm[unit]); +} + +/* + * ipxcp_close - Take IPXCP down. + */ +static void +ipxcp_close(unit, reason) + int unit; + char *reason; +{ + fsm_close(&ipxcp_fsm[unit], reason); +} + + +/* + * ipxcp_lowerup - The lower layer is up. + */ +static void +ipxcp_lowerup(unit) + int unit; +{ + fsm_lowerup(&ipxcp_fsm[unit]); +} + + +/* + * ipxcp_lowerdown - The lower layer is down. + */ +static void +ipxcp_lowerdown(unit) + int unit; +{ + fsm_lowerdown(&ipxcp_fsm[unit]); +} + + +/* + * ipxcp_input - Input IPXCP packet. + */ +static void +ipxcp_input(unit, p, len) + int unit; + u_char *p; + int len; +{ + fsm_input(&ipxcp_fsm[unit], p, len); +} + + +/* + * ipxcp_protrej - A Protocol-Reject was received for IPXCP. + * + * Pretend the lower layer went down, so we shut up. + */ +static void +ipxcp_protrej(unit) + int unit; +{ + fsm_lowerdown(&ipxcp_fsm[unit]); +} + + +/* + * ipxcp_resetci - Reset our CI. + */ +static void +ipxcp_resetci(f) + fsm *f; +{ + wo->req_node = wo->neg_node && ao->neg_node; + wo->req_nn = wo->neg_nn && ao->neg_nn; + + if (wo->our_network == 0) { + wo->neg_node = 1; + ao->accept_network = 1; + } +/* + * If our node number is zero then change it. + */ + if (zero_node (wo->our_node)) { + inc_node (wo->our_node); + ao->accept_local = 1; + wo->neg_node = 1; + } +/* + * If his node number is zero then change it. + */ + if (zero_node (wo->his_node)) { + inc_node (wo->his_node); + ao->accept_remote = 1; + } +/* + * If no routing agent was specified then we do RIP/SAP according to the + * RFC documents. If you have specified something then OK. Otherwise, we + * do RIP/SAP. + */ + if (ao->router == 0) { + ao->router |= BIT(RIP_SAP); + wo->router |= BIT(RIP_SAP); + } + + /* Always specify a routing protocol unless it was REJected. */ + wo->neg_router = 1; +/* + * Start with these default values + */ + *go = *wo; +} + +/* + * ipxcp_cilen - Return length of our CI. + */ + +static int +ipxcp_cilen(f) + fsm *f; +{ + int len; + + len = go->neg_nn ? CILEN_NETN : 0; + len += go->neg_node ? CILEN_NODEN : 0; + len += go->neg_name ? CILEN_NAME + strlen (go->name) - 1 : 0; + + /* RFC says that defaults should not be included. */ + if (go->neg_router && to_external(go->router) != RIP_SAP) + len += CILEN_PROTOCOL; + + return (len); +} + + +/* + * ipxcp_addci - Add our desired CIs to a packet. + */ +static void +ipxcp_addci(f, ucp, lenp) + fsm *f; + u_char *ucp; + int *lenp; +{ +/* + * Add the options to the record. + */ + if (go->neg_nn) { + PUTCHAR (IPX_NETWORK_NUMBER, ucp); + PUTCHAR (CILEN_NETN, ucp); + PUTLONG (go->our_network, ucp); + } + + if (go->neg_node) { + int indx; + PUTCHAR (IPX_NODE_NUMBER, ucp); + PUTCHAR (CILEN_NODEN, ucp); + for (indx = 0; indx < sizeof (go->our_node); ++indx) + PUTCHAR (go->our_node[indx], ucp); + } + + if (go->neg_name) { + int cilen = strlen (go->name); + int indx; + PUTCHAR (IPX_ROUTER_NAME, ucp); + PUTCHAR (CILEN_NAME + cilen - 1, ucp); + for (indx = 0; indx < cilen; ++indx) + PUTCHAR (go->name [indx], ucp); + } + + if (go->neg_router) { + short external = to_external (go->router); + if (external != RIP_SAP) { + PUTCHAR (IPX_ROUTER_PROTOCOL, ucp); + PUTCHAR (CILEN_PROTOCOL, ucp); + PUTSHORT (external, ucp); + } + } +} + +/* + * ipxcp_ackci - Ack our CIs. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +ipxcp_ackci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + u_short cilen, citype, cishort; + u_char cichar; + u_int32_t cilong; + +#define ACKCIVOID(opt, neg) \ + if (neg) { \ + if ((len -= CILEN_VOID) < 0) \ + break; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_VOID || \ + citype != opt) \ + break; \ + } + +#define ACKCICOMPLETE(opt,neg) ACKCIVOID(opt, neg) + +#define ACKCICHARS(opt, neg, val, cnt) \ + if (neg) { \ + int indx, count = cnt; \ + len -= (count + 2); \ + if (len < 0) \ + break; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != (count + 2) || \ + citype != opt) \ + break; \ + for (indx = 0; indx < count; ++indx) {\ + GETCHAR(cichar, p); \ + if (cichar != ((u_char *) &val)[indx]) \ + break; \ + }\ + if (indx != count) \ + break; \ + } + +#define ACKCINODE(opt,neg,val) ACKCICHARS(opt,neg,val,sizeof(val)) +#define ACKCINAME(opt,neg,val) ACKCICHARS(opt,neg,val,strlen(val)) + +#define ACKCINETWORK(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_NETN) < 0) \ + break; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_NETN || \ + citype != opt) \ + break; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + break; \ + } + +#define ACKCIPROTO(opt, neg, val) \ + if (neg) { \ + if (len < 2) \ + break; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_PROTOCOL || citype != opt) \ + break; \ + len -= cilen; \ + if (len < 0) \ + break; \ + GETSHORT(cishort, p); \ + if (cishort != to_external (val) || cishort == RIP_SAP) \ + break; \ + } +/* + * Process the ACK frame in the order in which the frame was assembled + */ + do { + ACKCINETWORK (IPX_NETWORK_NUMBER, go->neg_nn, go->our_network); + ACKCINODE (IPX_NODE_NUMBER, go->neg_node, go->our_node); + ACKCINAME (IPX_ROUTER_NAME, go->neg_name, go->name); + ACKCIPROTO (IPX_ROUTER_PROTOCOL, go->neg_router, go->router); + ACKCIPROTO (IPX_ROUTER_PROTOCOL, go->neg_router, go->router); + ACKCIPROTO (IPX_ROUTER_PROTOCOL, go->neg_router, go->router); +/* + * This is the end of the record. + */ + if (len == 0) + return (1); + } while (0); +/* + * The frame is invalid + */ + IPXCPDEBUG((LOG_INFO, "ipxcp_ackci: received bad Ack!")); + return (0); +} + +/* + * ipxcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if IPXCP is in the OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ + +static int +ipxcp_nakci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + u_char citype, cilen, *next; + u_short s; + u_int32_t l; + ipxcp_options no; /* options we've seen Naks for */ + ipxcp_options try; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try = *go; + + while (len > CILEN_VOID) { + GETCHAR (citype, p); + GETCHAR (cilen, p); + len -= cilen; + if (len < 0) + goto bad; + next = &p [cilen - CILEN_VOID]; + + switch (citype) { + case IPX_NETWORK_NUMBER: + if (!go->neg_nn || no.neg_nn || (cilen != CILEN_NETN)) + goto bad; + no.neg_nn = 1; + + GETLONG(l, p); + IPXCPDEBUG((LOG_INFO, "local IP address %d", l)); + if (l && ao->accept_network) + try.our_network = l; + break; + + case IPX_NODE_NUMBER: + if (!go->neg_node || no.neg_node || (cilen != CILEN_NODEN)) + goto bad; + no.neg_node = 1; + + IPXCPDEBUG((LOG_INFO, + "local node number %02X%02X%02X%02X%02X%02X", + NODE(p))); + + if (!zero_node (p) && ao->accept_local && + ! compare_node (p, ho->his_node)) + copy_node (p, try.our_node); + break; + + /* This has never been sent. Ignore the NAK frame */ + case IPX_COMPRESSION_PROTOCOL: + goto bad; + + case IPX_ROUTER_PROTOCOL: + if (!go->neg_router || (cilen < CILEN_PROTOCOL)) + goto bad; + + GETSHORT (s, p); + if (s > 15) /* This is just bad, but ignore for now. */ + break; + + s = BIT(s); + if (no.router & s) /* duplicate NAKs are always bad */ + goto bad; + + if (no.router == 0) /* Reset on first NAK only */ + try.router = 0; + + no.router |= s; + try.router |= s; + try.neg_router = 1; + + IPXCPDEBUG((LOG_INFO, "Router protocol number %d", s)); + break; + + /* These, according to the RFC, must never be NAKed. */ + case IPX_ROUTER_NAME: + case IPX_COMPLETE: + goto bad; + + /* These are for options which we have not seen. */ + default: + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) + goto bad; + + /* + * Do not permit the peer to force a router protocol which we do not + * support. However, default to the condition that will accept "NONE". + */ + try.router &= (ao->router | BIT(IPX_NONE)); + if (try.router == 0 && ao->router != 0) + try.router = BIT(IPX_NONE); + + if (try.router != 0) + try.neg_router = 1; + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != OPENED) + *go = try; + + return 1; + +bad: + IPXCPDEBUG((LOG_INFO, "ipxcp_nakci: received bad Nak!")); + return 0; +} + +/* + * ipxcp_rejci - Reject some of our CIs. + */ +static int +ipxcp_rejci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + u_short cilen, citype, cishort; + u_char cichar; + u_int32_t cilong; + ipxcp_options try; /* options to request next time */ + +#define REJCINETWORK(opt, neg, val) \ + if (neg && p[0] == opt) { \ + if ((len -= CILEN_NETN) < 0) \ + break; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_NETN || \ + citype != opt) \ + break; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + break; \ + IPXCPDEBUG((LOG_INFO,"ipxcp_rejci rejected long opt %d", opt)); \ + neg = 0; \ + } + +#define REJCICHARS(opt, neg, val, cnt) \ + if (neg && p[0] == opt) { \ + int indx, count = cnt; \ + len -= (count + 2); \ + if (len < 0) \ + break; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != (count + 2) || \ + citype != opt) \ + break; \ + for (indx = 0; indx < count; ++indx) {\ + GETCHAR(cichar, p); \ + if (cichar != ((u_char *) &val)[indx]) \ + break; \ + }\ + if (indx != count) \ + break; \ + IPXCPDEBUG((LOG_INFO,"ipxcp_rejci rejected opt %d", opt)); \ + neg = 0; \ + } + +#define REJCINODE(opt,neg,val) REJCICHARS(opt,neg,val,sizeof(val)) +#define REJCINAME(opt,neg,val) REJCICHARS(opt,neg,val,strlen(val)) + +#define REJCIVOID(gpt, neg! \ + if (neg && p[0] == opt) { \ + if ((len -= CILEN_VOID) < 0) \ + break; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_VOID || citype != opt) \ + break; \ + IPXCPDEBUG((LOG_INFO, "ipxcp_rejci rejected void opt %d", opt)); \ + neg = 0; \ + } + +/* a reject for RIP/SAP is invalid since we don't send it and you can't + reject something which is not sent. (You can NAK, but you can't REJ.) */ +#define REJCIPROTO(opt, neg, val, bit) \ + if (neg && p[0] == opt) { \ + if ((len -= CILEN_PROTOCOL) < 0) \ + break; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_PROTOCOL) \ + break; \ + GETSHORT(cishort, p); \ + if (cishort != to_external (val) || cishort == RIP_SAP) \ + break; \ + IPXCPDEBUG((LOG_INFO, "ipxcp_rejci short opt %d", opt)); \ + neg = 0; \ + } +/* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + try = *go; + + do { + REJCINETWORK (IPX_NETWORK_NUMBER, try.neg_nn, try.our_network); + REJCINODE (IPX_NODE_NUMBER, try.neg_node, try.our_node); + REJCINAME (IPX_ROUTER_NAME, try.neg_name, try.name); + REJCIPROTO (IPX_ROUTER_PROTOCOL, try.neg_router, try.router, 0); +/* + * This is the end of the record. + */ + if (len == 0) { + if (f->state != OPENED) + *go = try; + return (1); + } + } while (0); +/* + * The frame is invalid at this point. + */ + IPXCPDEBUG((LOG_INFO, "ipxcp_rejci: received bad Reject!")); + return 0; +} + +/* + * ipxcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +ipxcp_reqci(f, inp, len, reject_if_disagree) + fsm *f; + u_char *inp; /* Requested CIs */ + int *len; /* Length of requested CIs */ + int reject_if_disagree; +{ + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ + u_short cishort; /* Parsed short value */ + u_int32_t cinetwork; /* Parsed address values */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPXCPDEBUG((LOG_INFO, "ipxcp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ +/* + * The network number must match. Choose the larger of the two. + */ + case IPX_NETWORK_NUMBER: + IPXCPDEBUG((LOG_INFO, "ipxcp: received Network Number request")); + + /* if we wont negotiate the network number or the length is wrong + then reject the option */ + if ( !ao->neg_nn || cilen != CILEN_NETN ) { + orc = CONFREJ; + break; + } + GETLONG(cinetwork, p); + IPXCPDEBUG((LOG_INFO,"Remote proposed IPX network number is %8Lx",tl)); + + /* If the network numbers match then acknowledge them. */ + if (cinetwork != 0) { + ho->his_network = cinetwork; + ho->neg_nn = 1; + if (wo->our_network == cinetwork) + break; +/* + * If the network number is not given or we don't accept their change or + * the network number is too small then NAK it. + */ + if (! ao->accept_network || cinetwork < wo->our_network) { + DECPTR (sizeof (u_int32_t), p); + PUTLONG (wo->our_network, p); + orc = CONFNAK; + } + break; + } +/* + * The peer sent '0' for the network. Give it ours if we have one. + */ + if (go->our_network != 0) { + DECPTR (sizeof (u_int32_t), p); + PUTLONG (wo->our_network, p); + orc = CONFNAK; +/* + * We don't have one. Reject the value. + */ + } else + orc = CONFREJ; + + break; +/* + * The node number is required + */ + case IPX_NODE_NUMBER: + IPXCPDEBUG((LOG_INFO, "ipxcp: received Node Number request")); + + /* if we wont negotiate the node number or the length is wrong + then reject the option */ + if ( cilen != CILEN_NODEN ) { + orc = CONFREJ; + break; + } + + copy_node (p, ho->his_node); + ho->neg_node = 1; +/* + * If the remote does not have a number and we do then NAK it with the value + * which we have for it. (We never have a default value of zero.) + */ + if (zero_node (ho->his_node)) { + orc = CONFNAK; + copy_node (wo->his_node, p); + INCPTR (sizeof (wo->his_node), p); + break; + } +/* + * If you have given me the expected network node number then I'll accept + * it now. + */ + if (compare_node (wo->his_node, ho->his_node)) { + orc = CONFACK; + ho->neg_node = 1; + INCPTR (sizeof (wo->his_node), p); + break; + } +/* + * If his node number is the same as ours then ask him to try the next + * value. + */ + if (compare_node (ho->his_node, go->our_node)) { + inc_node (ho->his_node); + orc = CONFNAK; + copy_node (ho->his_node, p); + INCPTR (sizeof (wo->his_node), p); + break; + } +/* + * If we don't accept a new value then NAK it. + */ + if (! ao->accept_remote) { + copy_node (wo->his_node, p); + INCPTR (sizeof (wo->his_node), p); + orc = CONFNAK; + break; + } + orc = CONFACK; + ho->neg_node = 1; + INCPTR (sizeof (wo->his_node), p); + break; +/* + * Compression is not desired at this time. It is always rejected. + */ + case IPX_COMPRESSION_PROTOCOL: + IPXCPDEBUG((LOG_INFO, "ipxcp: received Compression Protocol request ")); + orc = CONFREJ; + break; +/* + * The routing protocol is a bitmask of various types. Any combination + * of the values RIP_SAP and NLSP are permissible. 'IPX_NONE' for no + * routing protocol must be specified only once. + */ + case IPX_ROUTER_PROTOCOL: + if ( !ao->neg_router || cilen < CILEN_PROTOCOL ) { + orc = CONFREJ; + break; + } + + GETSHORT (cishort, p); + IPXCPDEBUG((LOG_INFO, + "Remote router protocol number 0x%04x", + cishort)); + + if (wo->neg_router == 0) { + wo->neg_router = 1; + wo->router = BIT(IPX_NONE); + } + + if ((cishort == IPX_NONE && ho->router != 0) || + (ho->router & BIT(IPX_NONE))) { + orc = CONFREJ; + break; + } + + cishort = BIT(cishort); + if (ho->router & cishort) { + orc = CONFREJ; + break; + } + + ho->router |= cishort; + ho->neg_router = 1; + + /* Finally do not allow a router protocol which we do not + support. */ + + if ((cishort & (ao->router | BIT(IPX_NONE))) == 0) { + int protocol; + + if (cishort == BIT(NLSP) && + (ao->router & BIT(RIP_SAP)) && + !wo->tried_rip) { + protocol = RIP_SAP; + wo->tried_rip = 1; + } else + protocol = IPX_NONE; + + DECPTR (sizeof (u_int16_t), p); + PUTSHORT (protocol, p); + orc = CONFNAK; + } + break; +/* + * The router name is advisorary. Just accept it if it is not too large. + */ + case IPX_ROUTER_NAME: + IPXCPDEBUG((LOG_INFO, "ipxcp: received Router Name request")); + if (cilen >= CILEN_NAME) { + int name_size = cilen - CILEN_NAME; + if (name_size > sizeof (ho->name)) + name_size = sizeof (ho->name) - 1; + memset (ho->name, 0, sizeof (ho->name)); + memcpy (ho->name, p, name_size); + ho->name [name_size] = '\0'; + ho->neg_name = 1; + orc = CONFACK; + break; + } + orc = CONFREJ; + break; +/* + * This is advisorary. + */ + case IPX_COMPLETE: + IPXCPDEBUG((LOG_INFO, "ipxcp: received Complete request")); + if (cilen != CILEN_COMPLETE) + orc = CONFREJ; + else { + ho->neg_complete = 1; + orc = CONFACK; + } + break; +/* + * All other entries are not known at this time. + */ + default: + IPXCPDEBUG((LOG_INFO, "ipxcp: received Complete request")); + orc = CONFREJ; + break; + } + +endswitch: + IPXCPDEBUG((LOG_INFO, " (%s)\n", CODENAME(orc))); + + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) /* Getting fed up with sending NAKs? */ + orc = CONFREJ; /* Get tough if so */ + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) + BCOPY(cip, ucp, cilen); /* Move it */ + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their address, and they didn't send their address, then we + * send a NAK with a IPX_NODE_NUMBER option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + + if (rc != CONFREJ && !ho->neg_node && + wo->req_nn && !reject_if_disagree) { + if (rc == CONFACK) { + rc = CONFNAK; + wo->req_nn = 0; /* don't ask again */ + ucp = inp; /* reset pointer */ + } + + if (zero_node (wo->his_node)) + inc_node (wo->his_node); + + PUTCHAR (IPX_NODE_NUMBER, ucp); + PUTCHAR (CILEN_NODEN, ucp); + copy_node (wo->his_node, ucp); + INCPTR (sizeof (wo->his_node), ucp); + } + + *len = ucp - inp; /* Compute output length */ + IPXCPDEBUG((LOG_INFO, "ipxcp: returning Configure-%s", CODENAME(rc))); + return (rc); /* Return final code */ +} + +/* + * ipxcp_up - IPXCP has come UP. + * + * Configure the IP network interface appropriately and bring it up. + */ + +static void +ipxcp_up(f) + fsm *f; +{ + int unit = f->unit; + + IPXCPDEBUG((LOG_INFO, "ipxcp: up")); + + /* The default router protocol is RIP/SAP. */ + if (ho->router == 0) + ho->router = BIT(RIP_SAP); + + if (go->router == 0) + go->router = BIT(RIP_SAP); + + /* Fetch the network number */ + if (!ho->neg_nn) + ho->his_network = wo->his_network; + + if (!ho->neg_node) + copy_node (wo->his_node, ho->his_node); + + if (!wo->neg_node && !go->neg_node) + copy_node (wo->our_node, go->our_node); + + if (zero_node (go->our_node)) { + static char errmsg[] = "Could not determine local IPX node address"; + IPXCPDEBUG((LOG_ERR, errmsg)); + ipxcp_close(f->unit, errmsg); + return; + } + + go->network = go->our_network; + if (ho->his_network != 0 && ho->his_network > go->network) + go->network = ho->his_network; + + if (go->network == 0) { + static char errmsg[] = "Can not determine network number"; + IPXCPDEBUG((LOG_ERR, errmsg)); + ipxcp_close (unit, errmsg); + return; + } + + /* bring the interface up */ + if (!sifup(unit)) { + IPXCPDEBUG((LOG_WARNING, "sifup failed")); + ipxcp_close(unit, "Interface configuration failed"); + return; + } + + /* set the network number for IPX */ + if (!sipxfaddr(unit, go->network, go->our_node)) { + IPXCPDEBUG((LOG_WARNING, "sipxfaddr failed")); + ipxcp_close(unit, "Interface configuration failed"); + return; + } + + /* + * Execute the ipx-up script, like this: + * /etc/ppp/ipx-up interface tty speed local-IPX remote-IPX + */ + + ipxcp_script (f, _PATH_IPXUP); +} + +/* + * ipxcp_down - IPXCP has gone DOWN. + * + * Take the IP network interface down, clear its addresses + * and delete routes through it. + */ + +static void +ipxcp_down(f) + fsm *f; +{ + IPXCPDEBUG((LOG_INFO, "ipxcp: down")); + + cipxfaddr (f->unit); + sifdown(f->unit); + ipxcp_script (f, _PATH_IPXDOWN); +} + + +/* + * ipxcp_script - Execute a script with arguments + * interface-name tty-name speed local-IPX remote-IPX networks. + */ +static void +ipxcp_script(f, script) + fsm *f; + char *script; +{ + char strspeed[32], strlocal[32], strremote[32]; + char strnetwork[32], strpid[32]; + char *argv[14], strproto_lcl[32], strproto_rmt[32]; + + sprintf (strpid, "%d", getpid()); + sprintf (strspeed, "%d", baud_rate); + + strproto_lcl[0] = '\0'; + if (go->neg_router && ((go->router & BIT(IPX_NONE)) == 0)) { + if (go->router & BIT(RIP_SAP)) + strcpy (strproto_lcl, "RIP "); + if (go->router & BIT(NLSP)) + strcat (strproto_lcl, "NLSP "); + } + + if (strproto_lcl[0] == '\0') + strcpy (strproto_lcl, "NONE "); + + strproto_lcl[strlen (strproto_lcl)-1] = '\0'; + + strproto_rmt[0] = '\0'; + if (ho->neg_router && ((ho->router & BIT(IPX_NONE)) == 0)) { + if (ho->router & BIT(RIP_SAP)) + strcpy (strproto_rmt, "RIP "); + if (ho->router & BIT(NLSP)) + strcat (strproto_rmt, "NLSP "); + } + + if (strproto_rmt[0] == '\0') + strcpy (strproto_rmt, "NONE "); + + strproto_rmt[strlen (strproto_rmt)-1] = '\0'; + + strcpy (strnetwork, ipx_ntoa (go->network)); + + sprintf (strlocal, + "%02X%02X%02X%02X%02X%02X", + NODE(go->our_node)); + + sprintf (strremote, + "%02X%02X%02X%02X%02X%02X", + NODE(ho->his_node)); + + argv[0] = script; + argv[1] = ifname; + argv[2] = devnam; + argv[3] = strspeed; + argv[4] = strnetwork; + argv[5] = strlocal; + argv[6] = strremote; + argv[7] = strproto_lcl; + argv[8] = strproto_rmt; + argv[9] = go->name; + argv[10] = ho->name; + argv[11] = ipparam; + argv[12] = strpid; + argv[13] = NULL; + run_program(script, argv, 0); +} + +/* + * ipxcp_printpkt - print the contents of an IPXCP packet. + */ +static char *ipxcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej" +}; + +static int +ipxcp_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int code, id, len, olen; + u_char *pstart, *optend; + u_short cishort; + u_int32_t cilong; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(ipxcp_codenames) / sizeof(char *)) + printer(arg, " %s", ipxcp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < CILEN_VOID || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case IPX_NETWORK_NUMBER: + if (olen == CILEN_NETN) { + p += 2; + GETLONG(cilong, p); + printer (arg, "network %s", ipx_ntoa (cilong)); + } + break; + case IPX_NODE_NUMBER: + if (olen == CILEN_NODEN) { + p += 2; + printer (arg, "node "); + while (p < optend) { + GETCHAR(code, p); + printer(arg, "%.2x", (int) (unsigned int) (unsigned char) code); + } + } + break; + case IPX_COMPRESSION_PROTOCOL: + if (olen == CILEN_COMPRESS) { + p += 2; + GETSHORT (cishort, p); + printer (arg, "compression %d", (int) cishort); + } + break; + case IPX_ROUTER_PROTOCOL: + if (olen == CILEN_PROTOCOL) { + p += 2; + GETSHORT (cishort, p); + printer (arg, "router proto %d", (int) cishort); + } + break; + case IPX_ROUTER_NAME: + if (olen >= CILEN_NAME) { + p += 2; + printer (arg, "router name \""); + while (p < optend) { + GETCHAR(code, p); + if (code >= 0x20 && code <= 0x7E) + printer (arg, "%c", (int) (unsigned int) (unsigned char) code); + else + printer (arg, " \\%.2x", (int) (unsigned int) (unsigned char) code); + } + printer (arg, "\""); + } + break; + case IPX_COMPLETE: + if (olen == CILEN_COMPLETE) { + p += 2; + printer (arg, "complete"); + } + break; + default: + break; + } + + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + print_string(p, len, printer, arg); + p += len; + len = 0; + } + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code); + } + + return p - pstart; +} +#endif /* ifdef IPX_CHANGE */ diff --git a/cpukit/pppd/ipxcp.h b/cpukit/pppd/ipxcp.h new file mode 100644 index 0000000000..139a7260b3 --- /dev/null +++ b/cpukit/pppd/ipxcp.h @@ -0,0 +1,71 @@ +/* + * ipxcp.h - IPX Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * Options. + */ +#define IPX_NETWORK_NUMBER 1 /* IPX Network Number */ +#define IPX_NODE_NUMBER 2 +#define IPX_COMPRESSION_PROTOCOL 3 +#define IPX_ROUTER_PROTOCOL 4 +#define IPX_ROUTER_NAME 5 +#define IPX_COMPLETE 6 + +/* Values for the router protocol */ +#define IPX_NONE 0 +#define RIP_SAP 2 +#define NLSP 4 + +typedef struct ipxcp_options { + int neg_node : 1; /* Negotiate IPX node number? */ + int req_node : 1; /* Ask peer to send IPX node number? */ + + int neg_nn : 1; /* Negotiate IPX network number? */ + int req_nn : 1; /* Ask peer to send IPX network number */ + + int neg_name : 1; /* Negotiate IPX router name */ + int neg_complete : 1; /* Negotiate completion */ + int neg_router : 1; /* Negotiate IPX router number */ + + int accept_local : 1; /* accept peer's value for ournode */ + int accept_remote : 1; /* accept peer's value for hisnode */ + int accept_network : 1; /* accept network number */ + + int tried_nlsp : 1; /* I have suggested NLSP already */ + int tried_rip : 1; /* I have suggested RIP/SAP already */ + + u_int32_t his_network; /* base network number */ + u_int32_t our_network; /* our value for network number */ + u_int32_t network; /* the final network number */ + + u_char his_node[6]; /* peer's node number */ + u_char our_node[6]; /* our node number */ + u_char name [48]; /* name of the router */ + int router; /* routing protocol */ +} ipxcp_options; + +extern fsm ipxcp_fsm[]; +extern ipxcp_options ipxcp_wantoptions[]; +extern ipxcp_options ipxcp_gotoptions[]; +extern ipxcp_options ipxcp_allowoptions[]; +extern ipxcp_options ipxcp_hisoptions[]; + +extern struct protent ipxcp_protent; diff --git a/cpukit/pppd/lcp.c b/cpukit/pppd/lcp.c new file mode 100644 index 0000000000..e58b3dcba2 --- /dev/null +++ b/cpukit/pppd/lcp.c @@ -0,0 +1,1858 @@ +/* + * lcp.c - PPP Link Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +/* static char rcsid[] = "$Id$"; */ +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pppd.h" +#include "fsm.h" +#include "lcp.h" +#include "chap.h" +#include "magic.h" + +/* global vars */ +fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/ +lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ +u_int32_t xmit_accm[NUM_PPP][8]; /* extended transmit ACCM */ + +static u_int32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */ +static u_int32_t lcp_echo_number = 0; /* ID number of next echo frame */ +static u_int32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */ + +static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void lcp_resetci __P((fsm *)); /* Reset our CI */ +static int lcp_cilen __P((fsm *)); /* Return length of our CI */ +static void lcp_addci __P((fsm *, u_char *, int *)); /* Add our CI to pkt */ +static int lcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */ +static int lcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */ +static int lcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */ +static int lcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv peer CI */ +static void lcp_up __P((fsm *)); /* We're UP */ +static void lcp_down __P((fsm *)); /* We're DOWN */ +static void lcp_starting __P((fsm *)); /* We need lower layer up */ +static void lcp_finished __P((fsm *)); /* We need lower layer down */ +static int lcp_extcode __P((fsm *, int, int, u_char *, int)); +static void lcp_rprotrej __P((fsm *, u_char *, int)); + +/* + * routines to send LCP echos to peer + */ + +static void lcp_echo_lowerup __P((int)); +static void lcp_echo_lowerdown __P((int)); +static void LcpEchoTimeout __P((void *)); +static void lcp_received_echo_reply __P((fsm *, int, u_char *, int)); +static void LcpSendEchoRequest __P((fsm *)); +static void LcpLinkFailure __P((fsm *)); +static void LcpEchoCheck __P((fsm *)); + +static fsm_callbacks lcp_callbacks = { /* LCP callback routines */ + lcp_resetci, /* Reset our Configuration Information */ + lcp_cilen, /* Length of our Configuration Information */ + lcp_addci, /* Add our Configuration Information */ + lcp_ackci, /* ACK our Configuration Information */ + lcp_nakci, /* NAK our Configuration Information */ + lcp_rejci, /* Reject our Configuration Information */ + lcp_reqci, /* Request peer's Configuration Information */ + lcp_up, /* Called when fsm reaches OPENED state */ + lcp_down, /* Called when fsm leaves OPENED state */ + lcp_starting, /* Called when we want the lower layer up */ + lcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + lcp_extcode, /* Called to handle LCP-specific codes */ + "LCP" /* String name of protocol */ +}; + +/* + * Protocol entry points. + * Some of these are called directly. + */ + +static void lcp_init __P((int)); +static void lcp_input __P((int, u_char *, int)); +static void lcp_protrej __P((int)); +static int lcp_printpkt __P((u_char *, int, + void (*) __P((void *, char *, ...)), void *)); + +struct protent lcp_protent = { + PPP_LCP, + lcp_init, + lcp_input, + lcp_protrej, + lcp_lowerup, + lcp_lowerdown, + lcp_open, + lcp_close, + lcp_printpkt, + NULL, + 1, + "LCP", + NULL, + NULL, + NULL +}; + +int lcp_loopbackfail = DEFLOOPBACKFAIL; + +/* + * Length of each type of configuration option (in octets) + */ +#define CILEN_VOID 2 +#define CILEN_CHAR 3 +#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */ +#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */ +#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */ +#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */ +#define CILEN_CBCP 3 + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + + +/* + * lcp_init - Initialize LCP. + */ +static void +lcp_init(unit) + int unit; +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *ao = &lcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_LCP; + f->callbacks = &lcp_callbacks; + + fsm_init(f); + + wo->passive = 0; + wo->silent = 0; + wo->restart = 0; /* Set to 1 in kernels or multi-line + implementations */ + wo->neg_mru = 1; + wo->mru = DEFMRU; + wo->neg_asyncmap = 0; + wo->asyncmap = 0; + wo->neg_chap = 0; /* Set to 1 on server */ + wo->neg_upap = 0; /* Set to 1 on server */ + wo->chap_mdtype = CHAP_DIGEST_MD5; + wo->neg_magicnumber = 1; + wo->neg_pcompression = 1; + wo->neg_accompression = 1; + wo->neg_lqr = 0; /* no LQR implementation yet */ + wo->neg_cbcp = 0; + + ao->neg_mru = 1; + ao->mru = MAXMRU; + ao->neg_asyncmap = 1; + ao->asyncmap = 0; + ao->neg_chap = 1; + ao->chap_mdtype = CHAP_DIGEST_MD5; + ao->neg_upap = 1; + ao->neg_magicnumber = 1; + ao->neg_pcompression = 1; + ao->neg_accompression = 1; + ao->neg_lqr = 0; /* no LQR implementation yet */ +#ifdef CBCP_SUPPORT + ao->neg_cbcp = 1; +#else + ao->neg_cbcp = 0; +#endif + + memset(xmit_accm[unit], 0, sizeof(xmit_accm[0])); + xmit_accm[unit][3] = 0x60000000; +} + + +/* + * lcp_open - LCP is allowed to come up. + */ +void +lcp_open(unit) + int unit; +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + + f->flags = 0; + if (wo->passive) + f->flags |= OPT_PASSIVE; + if (wo->silent) + f->flags |= OPT_SILENT; + fsm_open(f); +} + + +/* + * lcp_close - Take LCP down. + */ +void +lcp_close(unit, reason) + int unit; + char *reason; +{ + fsm *f = &lcp_fsm[unit]; + + if (phase != PHASE_DEAD) + phase = PHASE_TERMINATE; + if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) { + /* + * This action is not strictly according to the FSM in RFC1548, + * but it does mean that the program terminates if you do a + * lcp_close() in passive/silent mode when a connection hasn't + * been established. + */ + f->state = CLOSED; + lcp_finished(f); + + } else + fsm_close(&lcp_fsm[unit], reason); +} + + +/* + * lcp_lowerup - The lower layer is up. + */ +void +lcp_lowerup(unit) + int unit; +{ + lcp_options *wo = &lcp_wantoptions[unit]; + + /* + * Don't use A/C or protocol compression on transmission, + * but accept A/C and protocol compressed packets + * if we are going to ask for A/C and protocol compression. + */ + ppp_set_xaccm(unit, xmit_accm[unit]); + ppp_send_config(unit, PPP_MRU, 0xffffffff, 0, 0); + ppp_recv_config(unit, PPP_MRU, 0xffffffff, + wo->neg_pcompression, wo->neg_accompression); + peer_mru[unit] = PPP_MRU; + lcp_allowoptions[unit].asyncmap = xmit_accm[unit][0]; + + fsm_lowerup(&lcp_fsm[unit]); +} + + +/* + * lcp_lowerdown - The lower layer is down. + */ +void +lcp_lowerdown(unit) + int unit; +{ + fsm_lowerdown(&lcp_fsm[unit]); +} + + +/* + * lcp_input - Input LCP packet. + */ +static void +lcp_input(unit, p, len) + int unit; + u_char *p; + int len; +{ + fsm *f = &lcp_fsm[unit]; + + fsm_input(f, p, len); +} + + +/* + * lcp_extcode - Handle a LCP-specific code. + */ +static int +lcp_extcode(f, code, id, inp, len) + fsm *f; + int code, id; + u_char *inp; + int len; +{ + u_char *magp; + + switch( code ){ + case PROTREJ: + lcp_rprotrej(f, inp, len); + break; + + case ECHOREQ: + if (f->state != OPENED) + break; + LCPDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d", id)); + magp = inp; + PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp); + fsm_sdata(f, ECHOREP, id, inp, len); + break; + + case ECHOREP: + lcp_received_echo_reply(f, id, inp, len); + break; + + case DISCREQ: + break; + + default: + return 0; + } + return 1; +} + + +/* + * lcp_rprotrej - Receive an Protocol-Reject. + * + * Figure out which protocol is rejected and inform it. + */ +static void +lcp_rprotrej(f, inp, len) + fsm *f; + u_char *inp; + int len; +{ + int i; + struct protent *protp; + u_short prot; + + LCPDEBUG((LOG_INFO, "lcp_rprotrej.")); + + if (len < sizeof (u_short)) { + LCPDEBUG((LOG_INFO, + "lcp_rprotrej: Rcvd short Protocol-Reject packet!")); + return; + } + + GETSHORT(prot, inp); + + LCPDEBUG((LOG_INFO, + "lcp_rprotrej: Rcvd Protocol-Reject packet for %x!", + prot)); + + /* + * Protocol-Reject packets received in any state other than the LCP + * OPENED state SHOULD be silently discarded. + */ + if( f->state != OPENED ){ + LCPDEBUG((LOG_INFO, "Protocol-Reject discarded: LCP in state %d", + f->state)); + return; + } + + /* + * Upcall the proper Protocol-Reject routine. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol == prot && protp->enabled_flag) { + (*protp->protrej)(f->unit); + return; + } + + syslog(LOG_WARNING, "Protocol-Reject for unsupported protocol 0x%x", + prot); +} + + +/* + * lcp_protrej - A Protocol-Reject was received. + */ +/*ARGSUSED*/ +static void +lcp_protrej(unit) + int unit; +{ + /* + * Can't reject LCP! + */ + LCPDEBUG((LOG_WARNING, + "lcp_protrej: Received Protocol-Reject for LCP!")); + fsm_protreject(&lcp_fsm[unit]); +} + + +/* + * lcp_sprotrej - Send a Protocol-Reject for some protocol. + */ +void +lcp_sprotrej(unit, p, len) + int unit; + u_char *p; + int len; +{ + /* + * Send back the protocol and the information field of the + * rejected packet. We only get here if LCP is in the OPENED state. + */ + p += 2; + len -= 2; + + fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, + p, len); +} + + +/* + * lcp_resetci - Reset our CI. + */ +static void +lcp_resetci(f) + fsm *f; +{ + lcp_wantoptions[f->unit].magicnumber = magic(); + lcp_wantoptions[f->unit].numloops = 0; + lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit]; + peer_mru[f->unit] = PPP_MRU; + auth_reset(f->unit); +} + + +/* + * lcp_cilen - Return length of our CI. + */ +static int +lcp_cilen(f) + fsm *f; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + +#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0) +#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0) +#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0) +#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0) +#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0) +#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0) + /* + * NB: we only ask for one of CHAP and UPAP, even if we will + * accept either. + */ + return (LENCISHORT(go->neg_mru && go->mru != DEFMRU) + + LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) + + LENCICHAP(go->neg_chap) + + LENCISHORT(!go->neg_chap && go->neg_upap) + + LENCILQR(go->neg_lqr) + + LENCICBCP(go->neg_cbcp) + + LENCILONG(go->neg_magicnumber) + + LENCIVOID(go->neg_pcompression) + + LENCIVOID(go->neg_accompression)); +} + + +/* + * lcp_addci - Add our desired CIs to a packet. + */ +static void +lcp_addci(f, ucp, lenp) + fsm *f; + u_char *ucp; + int *lenp; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char *start_ucp = ucp; + +#define ADDCIVOID(opt, neg) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_VOID, ucp); \ + } +#define ADDCISHORT(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_SHORT, ucp); \ + PUTSHORT(val, ucp); \ + } +#define ADDCICHAP(opt, neg, val, digest) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAP, ucp); \ + PUTSHORT(val, ucp); \ + PUTCHAR(digest, ucp); \ + } +#define ADDCILONG(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LONG, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCILQR(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LQR, ucp); \ + PUTSHORT(PPP_LQR, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCICHAR(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAR, ucp); \ + PUTCHAR(val, ucp); \ + } + + ADDCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru); + ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, + go->asyncmap); + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + if (ucp - start_ucp != *lenp) { + /* this should never happen, because peer_mtu should be 1500 */ + syslog(LOG_ERR, "Bug in lcp_addci: wrong length") +; + } +} + + +/* + * lcp_ackci - Ack our CIs. + * This should not modify any state if the Ack is bad. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +lcp_ackci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cilen, citype, cichar; + u_short cishort; + u_int32_t cilong; + + /* + * CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define ACKCIVOID(opt, neg) \ + if (neg) { \ + if ((len -= CILEN_VOID) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_VOID || \ + citype != opt) \ + goto bad; \ + } +#define ACKCISHORT(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_SHORT) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_SHORT || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } +#define ACKCICHAR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_CHAR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAR || \ + citype != opt) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != val) \ + goto bad; \ + } +#define ACKCICHAP(opt, neg, val, digest) \ + if (neg) { \ + if ((len -= CILEN_CHAP) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAP || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != digest) \ + goto bad; \ + } +#define ACKCILONG(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LONG) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LONG || \ + citype != opt) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } +#define ACKCILQR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LQR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LQR || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != PPP_LQR) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } + + ACKCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru); + ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, + go->asyncmap); + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); +bad: + LCPDEBUG((LOG_WARNING, "lcp_acki: received bad Ack!")); + return (0); +} + + +/* + * lcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if LCP is in the OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +lcp_nakci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *wo = &lcp_wantoptions[f->unit]; + u_char citype, cichar, *next; + u_short cishort; + u_int32_t cilong; + lcp_options no; /* options we've seen Naks for */ + lcp_options try; /* options to request next time */ + int looped_back = 0; + int cilen; + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIVOID(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAP(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAR && \ + p[1] == CILEN_CHAR && \ + p[0] == opt) { \ + len -= CILEN_CHAR; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCISHORT(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILONG(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILQR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } + + /* + * We don't care if they want to send us smaller packets than + * we want. Therefore, accept any MRU less than what we asked for, + * but then ignore the new value when setting the MRU in the kernel. + * If they send us a bigger MRU than what we asked, accept it, up to + * the limit of the default MRU we'd get if we didn't negotiate. + */ + if (go->neg_mru && go->mru != DEFMRU) { + NAKCISHORT(CI_MRU, neg_mru, + if (cishort <= wo->mru || cishort <= DEFMRU) + try.mru = cishort; + ); + } + + /* + * Add any characters they want to our (receive-side) asyncmap. + */ + if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) { + NAKCILONG(CI_ASYNCMAP, neg_asyncmap, + try.asyncmap = go->asyncmap | cilong; + ); + } + + /* + * If they've nak'd our authentication-protocol, check whether + * they are proposing a different protocol, or a different + * hash algorithm for CHAP. + */ + if ((go->neg_chap || go->neg_upap) + && len >= CILEN_SHORT + && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) { + cilen = p[1]; + len -= cilen; + no.neg_chap = go->neg_chap; + no.neg_upap = go->neg_upap; + INCPTR(2, p); + GETSHORT(cishort, p); + if (cishort == PPP_PAP && cilen == CILEN_SHORT) { + /* + * If we were asking for CHAP, they obviously don't want to do it. + * If we weren't asking for CHAP, then we were asking for PAP, + * in which case this Nak is bad. + */ + if (!go->neg_chap) + goto bad; + try.neg_chap = 0; + + } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { + GETCHAR(cichar, p); + if (go->neg_chap) { + /* + * We were asking for CHAP/MD5; they must want a different + * algorithm. If they can't do MD5, we'll have to stop + * asking for CHAP. + */ + if (cichar != go->chap_mdtype) + try.neg_chap = 0; + } else { + /* + * Stop asking for PAP if we were asking for it. + */ + try.neg_upap = 0; + } + + } else { + /* + * We don't recognize what they're suggesting. + * Stop asking for what we were asking for. + */ + if (go->neg_chap) + try.neg_chap = 0; + else + try.neg_upap = 0; + p += cilen - CILEN_SHORT; + } + } + + /* + * If they can't cope with our link quality protocol, we'll have + * to stop asking for LQR. We haven't got any other protocol. + * If they Nak the reporting period, take their value XXX ? + */ + NAKCILQR(CI_QUALITY, neg_lqr, + if (cishort != PPP_LQR) + try.neg_lqr = 0; + else + try.lqr_period = cilong; + ); + + /* + * Only implementing CBCP...not the rest of the callback options + */ + NAKCICHAR(CI_CALLBACK, neg_cbcp, + try.neg_cbcp = 0; + ); + + /* + * Check for a looped-back line. + */ + NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, + try.magicnumber = magic(); + looped_back = 1; + ); + + /* + * Peer shouldn't send Nak for protocol compression or + * address/control compression requests; they should send + * a Reject instead. If they send a Nak, treat it as a Reject. + */ + NAKCIVOID(CI_PCOMPRESSION, neg_pcompression, + try.neg_pcompression = 0; + ); + NAKCIVOID(CI_ACCOMPRESSION, neg_accompression, + try.neg_accompression = 0; + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If we see an option that we requested, or one we've already seen + * in this packet, then this packet is bad. + * If we wanted to respond by starting to negotiate on the requested + * option(s), we could, but we don't, because except for the + * authentication type and quality protocol, if we are not negotiating + * an option, it is because we were told not to. + * For the authentication type, the Nak from the peer means + * `let me authenticate myself with you' which is a bit pointless. + * For the quality protocol, the Nak means `ask me to send you quality + * reports', but if we didn't ask for them, we don't want them. + * An option we don't recognize represents the peer asking to + * negotiate some option we don't support, so ignore it. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if (cilen < CILEN_VOID || (len -= cilen) < 0) + goto bad; + next = p + cilen - 2; + + switch (citype) { + case CI_MRU: + if ((go->neg_mru && go->mru != DEFMRU) + || no.neg_mru || cilen != CILEN_SHORT) + goto bad; + GETSHORT(cishort, p); + if (cishort < DEFMRU) + try.mru = cishort; + break; + case CI_ASYNCMAP: + if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) + || no.neg_asyncmap || cilen != CILEN_LONG) + goto bad; + break; + case CI_AUTHTYPE: + if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) + goto bad; + break; + case CI_MAGICNUMBER: + if (go->neg_magicnumber || no.neg_magicnumber || + cilen != CILEN_LONG) + goto bad; + break; + case CI_PCOMPRESSION: + if (go->neg_pcompression || no.neg_pcompression + || cilen != CILEN_VOID) + goto bad; + break; + case CI_ACCOMPRESSION: + if (go->neg_accompression || no.neg_accompression + || cilen != CILEN_VOID) + goto bad; + break; + case CI_QUALITY: + if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) + goto bad; + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) + goto bad; + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != OPENED) { + if (looped_back) { + if (++try.numloops >= lcp_loopbackfail) { + syslog(LOG_NOTICE, "Serial line is looped back."); + lcp_close(f->unit, "Loopback detected"); + } + } else + try.numloops = 0; + *go = try; + } + + return 1; + +bad: + LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!")); + return 0; +} + + +/* + * lcp_rejci - Peer has Rejected some of our CIs. + * This should not modify any state if the Reject is bad + * or if LCP is in the OPENED state. + * + * Returns: + * 0 - Reject was bad. + * 1 - Reject was good. + */ +static int +lcp_rejci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cichar; + u_short cishort; + u_int32_t cilong; + lcp_options try; /* options to request next time */ + + try = *go; + + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIVOID(opt, neg) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO, "lcp_rejci rejected void opt %d", opt)); \ + } +#define REJCISHORT(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected short opt %d", opt)); \ + } +#define REJCICHAP(opt, neg, val, digest) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cishort != val || cichar != digest) \ + goto bad; \ + try.neg = 0; \ + try.neg_upap = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected chap opt %d", opt)); \ + } +#define REJCILONG(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cilong != val) \ + goto bad; \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected long opt %d", opt)); \ + } +#define REJCILQR(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cishort != PPP_LQR || cilong != val) \ + goto bad; \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected LQR opt %d", opt)); \ + } +#define REJCICBCP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CBCP && \ + p[1] == CILEN_CBCP && \ + p[0] == opt) { \ + len -= CILEN_CBCP; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cichar != val) \ + goto bad; \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci rejected Callback opt %d", opt)); \ + } + + REJCISHORT(CI_MRU, neg_mru, go->mru); + REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); + REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype); + if (!go->neg_chap) { + REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); + } + REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); + REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT); + REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); + REJCIVOID(CI_PCOMPRESSION, neg_pcompression); + REJCIVOID(CI_ACCOMPRESSION, neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + /* + * Now we can update state. + */ + if (f->state != OPENED) + *go = try; + return 1; + +bad: + LCPDEBUG((LOG_WARNING, "lcp_rejci: received bad Reject!")); + return 0; +} + + +/* + * lcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +lcp_reqci(f, inp, lenp, reject_if_disagree) + fsm *f; + u_char *inp; /* Requested CIs */ + int *lenp; /* Length of requested CIs */ + int reject_if_disagree; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + u_char *cip, *next; /* Pointer to current and next CIs */ + int cilen, citype, cichar; /* Parsed len, type, char value */ + u_short cishort; /* Parsed short value */ + u_int32_t cilong; /* Parse long value */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *rejp; /* Pointer to next char in reject frame */ + u_char *nakp; /* Pointer to next char in Nak frame */ + int l = *lenp; /* Length left */ + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + nakp = nak_buffer; + rejp = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + citype = 0; + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_MRU: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MRU")); + if (!ao->neg_mru || /* Allow option? */ + cilen != CILEN_SHORT) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETSHORT(cishort, p); /* Parse MRU */ + LCPDEBUG((LOG_INFO, "(%d)", cishort)); + + /* + * He must be able to receive at least our minimum. + * No need to check a maximum. If he sends a large number, + * we'll just ignore it. + */ + if (cishort < MINMRU) { + orc = CONFNAK; /* Nak CI */ + PUTCHAR(CI_MRU, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(MINMRU, nakp); /* Give him a hint */ + break; + } + ho->neg_mru = 1; /* Remember he sent MRU */ + ho->mru = cishort; /* And remember value */ + break; + + case CI_ASYNCMAP: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ASYNCMAP")); + if (!ao->neg_asyncmap || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + LCPDEBUG((LOG_INFO, "(%x)", (unsigned int) cilong)); + + /* + * Asyncmap must have set at least the bits + * which are set in lcp_allowoptions[unit].asyncmap. + */ + if ((ao->asyncmap & ~cilong) != 0) { + orc = CONFNAK; + PUTCHAR(CI_ASYNCMAP, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(ao->asyncmap | cilong, nakp); + break; + } + ho->neg_asyncmap = 1; + ho->asyncmap = cilong; + break; + + case CI_AUTHTYPE: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd AUTHTYPE")); + if (cilen < CILEN_SHORT || + !(ao->neg_upap || ao->neg_chap)) { + /* + * Reject the option if we're not willing to authenticate. + */ + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + LCPDEBUG((LOG_INFO, "(%x)", cishort)); + + /* + * Authtype must be UPAP or CHAP. + * + * Note: if both ao->neg_upap and ao->neg_chap are set, + * and the peer sends a Configure-Request with two + * authenticate-protocol requests, one for CHAP and one + * for UPAP, then we will reject the second request. + * Whether we end up doing CHAP or UPAP depends then on + * the ordering of the CIs in the peer's Configure-Request. + */ + + if (cishort == PPP_PAP) { + if (ho->neg_chap || /* we've already accepted CHAP */ + cilen != CILEN_SHORT) { + LCPDEBUG((LOG_WARNING, + "lcp_reqci: rcvd AUTHTYPE PAP, rejecting...")); + orc = CONFREJ; + break; + } + if (!ao->neg_upap) { /* we don't want to do PAP */ + orc = CONFNAK; /* NAK it and suggest CHAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } + ho->neg_upap = 1; + break; + } + if (cishort == PPP_CHAP) { + if (ho->neg_upap || /* we've already accepted PAP */ + cilen != CILEN_CHAP) { + LCPDEBUG((LOG_INFO, + "lcp_reqci: rcvd AUTHTYPE CHAP, rejecting...")); + orc = CONFREJ; + break; + } + if (!ao->neg_chap) { /* we don't want to do CHAP */ + orc = CONFNAK; /* NAK it and suggest PAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + break; + } + GETCHAR(cichar, p); /* get digest type*/ + if (cichar != CHAP_DIGEST_MD5 +#ifdef CHAPMS + && cichar != CHAP_MICROSOFT +#endif + ) { + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } + ho->chap_mdtype = cichar; /* save md type */ + ho->neg_chap = 1; + break; + } + + /* + * We don't recognize the protocol they're asking for. + * Nak it with something we're willing to do. + * (At this point we know ao->neg_upap || ao->neg_chap.) + */ + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + if (ao->neg_chap) { + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + } else { + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + } + break; + + case CI_QUALITY: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd QUALITY")); + if (!ao->neg_lqr || + cilen != CILEN_LQR) { + orc = CONFREJ; + break; + } + + GETSHORT(cishort, p); + GETLONG(cilong, p); + LCPDEBUG((LOG_INFO, "(%x %x)", cishort, (unsigned int) cilong)); + + /* + * Check the protocol and the reporting period. + * XXX When should we Nak this, and what with? + */ + if (cishort != PPP_LQR) { + orc = CONFNAK; + PUTCHAR(CI_QUALITY, nakp); + PUTCHAR(CILEN_LQR, nakp); + PUTSHORT(PPP_LQR, nakp); + PUTLONG(ao->lqr_period, nakp); + break; + } + break; + + case CI_MAGICNUMBER: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MAGICNUMBER")); + if (!(ao->neg_magicnumber || go->neg_magicnumber) || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + LCPDEBUG((LOG_INFO, "(%x)", (unsigned int) cilong)); + + /* + * He must have a different magic number. + */ + if (go->neg_magicnumber && + cilong == go->magicnumber) { + cilong = magic(); /* Don't put magic() inside macro! */ + orc = CONFNAK; + PUTCHAR(CI_MAGICNUMBER, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(cilong, nakp); + break; + } + ho->neg_magicnumber = 1; + ho->magicnumber = cilong; + break; + + + case CI_PCOMPRESSION: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd PCOMPRESSION")); + if (!ao->neg_pcompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_pcompression = 1; + break; + + case CI_ACCOMPRESSION: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ACCOMPRESSION")); + if (!ao->neg_accompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_accompression = 1; + break; + + default: + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd unknown option %d", + citype)); + orc = CONFREJ; + break; + } + +endswitch: + LCPDEBUG((LOG_INFO, " (%s)", CODENAME(orc))); + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree /* Getting fed up with sending NAKs? */ + && citype != CI_MAGICNUMBER) { + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + rc = CONFNAK; + } + } + if (orc == CONFREJ) { /* Reject this CI */ + rc = CONFREJ; + if (cip != rejp) /* Need to move rejected CI? */ + BCOPY(cip, rejp, cilen); /* Move it */ + INCPTR(cilen, rejp); /* Update output pointer */ + } + } + + /* + * If we wanted to send additional NAKs (for unsent CIs), the + * code would go here. The extra NAKs would go at *nakp. + * At present there are no cases where we want to ask the + * peer to negotiate an option. + */ + + switch (rc) { + case CONFACK: + *lenp = next - inp; + break; + case CONFNAK: + /* + * Copy the Nak'd options from the nak_buffer to the caller's buffer. + */ + *lenp = nakp - nak_buffer; + BCOPY(nak_buffer, inp, *lenp); + break; + case CONFREJ: + *lenp = rejp - inp; + break; + } + + LCPDEBUG((LOG_INFO, "lcp_reqci: returning CONF%s.", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +/* + * lcp_up - LCP has come UP. + */ +static void +lcp_up(f) + fsm *f; +{ + lcp_options *wo = &lcp_wantoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + + if (!go->neg_magicnumber) + go->magicnumber = 0; + if (!ho->neg_magicnumber) + ho->magicnumber = 0; + + /* + * Set our MTU to the smaller of the MTU we wanted and + * the MRU our peer wanted. If we negotiated an MRU, + * set our MRU to the larger of value we wanted and + * the value we got in the negotiation. + */ + ppp_send_config(f->unit, MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)), + (ho->neg_asyncmap? ho->asyncmap: 0xffffffff), + ho->neg_pcompression, ho->neg_accompression); + ppp_recv_config(f->unit, (go->neg_mru? MAX(wo->mru, go->mru): PPP_MRU), + (go->neg_asyncmap? go->asyncmap: 0xffffffff), + go->neg_pcompression, go->neg_accompression); + + if (ho->neg_mru) + peer_mru[f->unit] = ho->mru; + + lcp_echo_lowerup(f->unit); /* Enable echo messages */ + + link_established(f->unit); +} + + +/* + * lcp_down - LCP has gone DOWN. + * + * Alert other protocols. + */ +static void +lcp_down(f) + fsm *f; +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + + lcp_echo_lowerdown(f->unit); + + link_down(f->unit); + + ppp_send_config(f->unit, PPP_MRU, 0xffffffff, 0, 0); + ppp_recv_config(f->unit, PPP_MRU, + (go->neg_asyncmap? go->asyncmap: 0xffffffff), + go->neg_pcompression, go->neg_accompression); + peer_mru[f->unit] = PPP_MRU; +} + + +/* + * lcp_starting - LCP needs the lower layer up. + */ +static void +lcp_starting(f) + fsm *f; +{ + link_required(f->unit); +} + + +/* + * lcp_finished - LCP has finished with the lower layer. + */ +static void +lcp_finished(f) + fsm *f; +{ + link_terminated(f->unit); +} + + +/* + * lcp_printpkt - print the contents of an LCP packet. + */ +static char *lcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", "ProtRej", + "EchoReq", "EchoRep", "DiscReq" +}; + +static int +lcp_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int code, id, len, olen; + u_char *pstart, *optend; + u_short cishort; + u_int32_t cilong; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *)) + printer(arg, " %s", lcp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_MRU: + if (olen == CILEN_SHORT) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "mru %d", cishort); + } + break; + case CI_ASYNCMAP: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "asyncmap 0x%x", cilong); + } + break; + case CI_AUTHTYPE: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "auth "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_PAP: + printer(arg, "pap"); + break; + case PPP_CHAP: + printer(arg, "chap"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_QUALITY: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "quality "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_LQR: + printer(arg, "lqr"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_CALLBACK: + if (olen >= CILEN_CHAR) { + p += 2; + printer(arg, "callback "); + GETSHORT(cishort, p); + switch (cishort) { + case CBCP_OPT: + printer(arg, "CBCP"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_MAGICNUMBER: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "magic 0x%x", cilong); + } + break; + case CI_PCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "pcomp"); + } + break; + case CI_ACCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "accomp"); + } + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + print_string(p, len, printer, arg); + p += len; + len = 0; + } + break; + + case ECHOREQ: + case ECHOREP: + case DISCREQ: + if (len >= 4) { + GETLONG(cilong, p); + printer(arg, " magic=0x%x", cilong); + p += 4; + len -= 4; + } + break; + } + + + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} + +/* + * Time to shut down the link because there is nothing out there. + */ + +static +void LcpLinkFailure (f) + fsm *f; +{ + if (f->state == OPENED) { + syslog(LOG_INFO, "No response to %d echo-requests", lcp_echos_pending); + syslog(LOG_NOTICE, "Serial link appears to be disconnected."); + lcp_close(f->unit, "Peer not responding"); + } +} + +/* + * Timer expired for the LCP echo requests from this process. + */ + +static void +LcpEchoCheck (f) + fsm *f; +{ + LcpSendEchoRequest (f); + + /* + * Start the timer for the next interval. + */ + assert (lcp_echo_timer_running==0); + TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval); + lcp_echo_timer_running = 1; +} + +/* + * LcpEchoTimeout - Timer expired on the LCP echo + */ + +static void +LcpEchoTimeout (arg) + void *arg; +{ + if (lcp_echo_timer_running != 0) { + lcp_echo_timer_running = 0; + LcpEchoCheck ((fsm *) arg); + } +} + +/* + * LcpEchoReply - LCP has received a reply to the echo + */ + +static void +lcp_received_echo_reply (f, id, inp, len) + fsm *f; + int id; u_char *inp; int len; +{ + u_int32_t magic; + + /* Check the magic number - don't count replies from ourselves. */ + if (len < 4) { + syslog(LOG_DEBUG, "lcp: received short Echo-Reply, length %d", len); + return; + } + GETLONG(magic, inp); + if (lcp_gotoptions[f->unit].neg_magicnumber + && magic == lcp_gotoptions[f->unit].magicnumber) { + syslog(LOG_WARNING, "appear to have received our own echo-reply!"); + return; + } + + /* Reset the number of outstanding echo frames */ + lcp_echos_pending = 0; +} + +/* + * LcpSendEchoRequest - Send an echo request frame to the peer + */ + +static void +LcpSendEchoRequest (f) + fsm *f; +{ + u_int32_t lcp_magic; + u_char pkt[4], *pktp; + + /* + * Detect the failure of the peer at this point. + */ + if (lcp_echo_fails != 0) { + if (lcp_echos_pending >= lcp_echo_fails) { + LcpLinkFailure(f); + lcp_echos_pending = 0; + } + } + + /* + * Make and send the echo request frame. + */ + if (f->state == OPENED) { + lcp_magic = lcp_gotoptions[f->unit].magicnumber; + pktp = pkt; + PUTLONG(lcp_magic, pktp); + fsm_sdata(f, ECHOREQ, lcp_echo_number++ & 0xFF, pkt, pktp - pkt); + ++lcp_echos_pending; + } +} + +/* + * lcp_echo_lowerup - Start the timer for the LCP frame + */ + +static void +lcp_echo_lowerup (unit) + int unit; +{ + fsm *f = &lcp_fsm[unit]; + + /* Clear the parameters for generating echo frames */ + lcp_echos_pending = 0; + lcp_echo_number = 0; + lcp_echo_timer_running = 0; + + /* If a timeout interval is specified then start the timer */ + if (lcp_echo_interval != 0) + LcpEchoCheck (f); +} + +/* + * lcp_echo_lowerdown - Stop the timer for the LCP frame + */ + +static void +lcp_echo_lowerdown (unit) + int unit; +{ + fsm *f = &lcp_fsm[unit]; + + if (lcp_echo_timer_running != 0) { + UNTIMEOUT (LcpEchoTimeout, f); + lcp_echo_timer_running = 0; + } +} diff --git a/cpukit/pppd/lcp.h b/cpukit/pppd/lcp.h new file mode 100644 index 0000000000..42260f3a63 --- /dev/null +++ b/cpukit/pppd/lcp.h @@ -0,0 +1,88 @@ +/* + * lcp.h - Link Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * Options. + */ +#define CI_MRU 1 /* Maximum Receive Unit */ +#define CI_ASYNCMAP 2 /* Async Control Character Map */ +#define CI_AUTHTYPE 3 /* Authentication Type */ +#define CI_QUALITY 4 /* Quality Protocol */ +#define CI_MAGICNUMBER 5 /* Magic Number */ +#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ +#define CI_CALLBACK 13 /* callback */ + +/* + * LCP-specific packet types. + */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ +#define CBCP_OPT 6 /* Use callback control protocol */ + +/* + * The state of options is described by an lcp_options structure. + */ +typedef struct lcp_options { + int passive : 1; /* Don't die if we don't get a response */ + int silent : 1; /* Wait for the other end to start first */ + int restart : 1; /* Restart vs. exit after close */ + int neg_mru : 1; /* Negotiate the MRU? */ + int neg_asyncmap : 1; /* Negotiate the async map? */ + int neg_upap : 1; /* Ask for UPAP authentication? */ + int neg_chap : 1; /* Ask for CHAP authentication? */ + int neg_magicnumber : 1; /* Ask for magic number? */ + int neg_pcompression : 1; /* HDLC Protocol Field Compression? */ + int neg_accompression : 1; /* HDLC Address/Control Field Compression? */ + int neg_lqr : 1; /* Negotiate use of Link Quality Reports */ + int neg_cbcp : 1; /* Negotiate use of CBCP */ + u_short mru; /* Value of MRU */ + u_char chap_mdtype; /* which MD type (hashing algorithm) */ + u_int32_t asyncmap; /* Value of async map */ + u_int32_t magicnumber; + int numloops; /* Number of loops during magic number neg. */ + u_int32_t lqr_period; /* Reporting period for LQR 1/100ths second */ +} lcp_options; + +extern fsm lcp_fsm[]; +extern lcp_options lcp_wantoptions[]; +extern lcp_options lcp_gotoptions[]; +extern lcp_options lcp_allowoptions[]; +extern lcp_options lcp_hisoptions[]; +extern u_int32_t xmit_accm[][8]; + +#define DEFMRU 1500 /* Try for this */ +#define MINMRU 128 /* No MRUs below this */ +#define MAXMRU 16384 /* Normally limit MRU to this */ + +void lcp_open __P((int)); +void lcp_close __P((int, char *)); +void lcp_lowerup __P((int)); +void lcp_lowerdown __P((int)); +void lcp_sprotrej __P((int, u_char *, int)); /* send protocol reject */ + +extern struct protent lcp_protent; + +/* Default number of times we receive our magic number from the peer + before deciding the link is looped-back. */ +#define DEFLOOPBACKFAIL 10 diff --git a/cpukit/pppd/magic.c b/cpukit/pppd/magic.c new file mode 100644 index 0000000000..67d6b6ca90 --- /dev/null +++ b/cpukit/pppd/magic.c @@ -0,0 +1,86 @@ +/* + * magic.c - PPP Magic Number routines. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +/* static char rcsid[] = "$Id$"; */ +#endif + +#include +#include +#include +#include +#include + +#include "pppd.h" +#include "magic.h" + +extern long mrand48 __P((void)); +extern void srand48 __P((long)); + +/* + * magic_init - Initialize the magic number generator. + * + * Attempts to compute a random number seed which will not repeat. + * The current method uses the current hostid, current process ID + * and current time, currently. + */ +void +magic_init() +{ + long seed; + struct timeval t; + + gettimeofday(&t, NULL); + seed = get_host_seed() ^ t.tv_sec ^ t.tv_usec ^ getpid(); + srand48(seed); +} + +/* + * magic - Returns the next magic number. + */ +u_int32_t +magic() +{ + return (u_int32_t) mrand48(); +} + +/* + * Substitute procedures for those systems which don't have + * drand48 et al. + */ +/* #include */ +double +drand48() +{ + return (double)rand() / (double)0x7fffffffL; /* 2**31-1 */ +} + +long +mrand48() +{ + return rand(); +} + +void +srand48(seedval) +long seedval; +{ + srand((int)seedval); +} + diff --git a/cpukit/pppd/magic.h b/cpukit/pppd/magic.h new file mode 100644 index 0000000000..1344626a38 --- /dev/null +++ b/cpukit/pppd/magic.h @@ -0,0 +1,23 @@ +/* + * magic.h - PPP Magic Number definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +void magic_init __P((void)); /* Initialize the magic number generator */ +u_int32_t magic __P((void)); /* Returns the next magic number */ diff --git a/cpukit/pppd/md4.c b/cpukit/pppd/md4.c new file mode 100644 index 0000000000..cda9f943d0 --- /dev/null +++ b/cpukit/pppd/md4.c @@ -0,0 +1,298 @@ +/* +** ******************************************************************** +** md4.c -- Implementation of MD4 Message Digest Algorithm ** +** Updated: 2/16/90 by Ronald L. Rivest ** +** (C) 1990 RSA Data Security, Inc. ** +** ******************************************************************** +*/ + +/* +** To use MD4: +** -- Include md4.h in your program +** -- Declare an MDstruct MD to hold the state of the digest +** computation. +** -- Initialize MD using MDbegin(&MD) +** -- For each full block (64 bytes) X you wish to process, call +** MD4Update(&MD,X,512) +** (512 is the number of bits in a full block.) +** -- For the last block (less than 64 bytes) you wish to process, +** MD4Update(&MD,X,n) +** where n is the number of bits in the partial block. A partial +** block terminates the computation, so every MD computation +** should terminate by processing a partial block, even if it +** has n = 0. +** -- The message digest is available in MD.buffer[0] ... +** MD.buffer[3]. (Least-significant byte of each word +** should be output first.) +** -- You can print out the digest using MDprint(&MD) +*/ + +/* Implementation notes: +** This implementation assumes that ints are 32-bit quantities. +*/ + +#define TRUE 1 +#define FALSE 0 + +/* Compile-time includes +*/ +#include +#include "md4.h" +#include "pppd.h" + +/* Compile-time declarations of MD4 "magic constants". +*/ +#define I0 0x67452301 /* Initial values for MD buffer */ +#define I1 0xefcdab89 +#define I2 0x98badcfe +#define I3 0x10325476 +#define C2 013240474631 /* round 2 constant = sqrt(2) in octal */ +#define C3 015666365641 /* round 3 constant = sqrt(3) in octal */ +/* C2 and C3 are from Knuth, The Art of Programming, Volume 2 +** (Seminumerical Algorithms), Second Edition (1981), Addison-Wesley. +** Table 2, page 660. +*/ + +#define fs1 3 /* round 1 shift amounts */ +#define fs2 7 +#define fs3 11 +#define fs4 19 +#define gs1 3 /* round 2 shift amounts */ +#define gs2 5 +#define gs3 9 +#define gs4 13 +#define hs1 3 /* round 3 shift amounts */ +#define hs2 9 +#define hs3 11 +#define hs4 15 + +/* Compile-time macro declarations for MD4. +** Note: The "rot" operator uses the variable "tmp". +** It assumes tmp is declared as unsigned int, so that the >> +** operator will shift in zeros rather than extending the sign bit. +*/ +#define f(X,Y,Z) ((X&Y) | ((~X)&Z)) +#define g(X,Y,Z) ((X&Y) | (X&Z) | (Y&Z)) +#define h(X,Y,Z) (X^Y^Z) +#define rot(X,S) (tmp=X,(tmp<>(32-S))) +#define ff(A,B,C,D,i,s) A = rot((A + f(B,C,D) + X[i]),s) +#define gg(A,B,C,D,i,s) A = rot((A + g(B,C,D) + X[i] + C2),s) +#define hh(A,B,C,D,i,s) A = rot((A + h(B,C,D) + X[i] + C3),s) + +/* MD4print(MDp) +** Print message digest buffer MDp as 32 hexadecimal digits. +** Order is from low-order byte of buffer[0] to high-order byte of +** buffer[3]. +** Each byte is printed with high-order hexadecimal digit first. +** This is a user-callable routine. +*/ +void +MD4Print(MDp) +MD4_CTX *MDp; +{ + int i,j; + for (i=0;i<4;i++) + for (j=0;j<32;j=j+8) + printf("%02x",(MDp->buffer[i]>>j) & 0xFF); +} + +/* MD4Init(MDp) +** Initialize message digest buffer MDp. +** This is a user-callable routine. +*/ +void +MD4Init(MDp) +MD4_CTX *MDp; +{ + int i; + MDp->buffer[0] = I0; + MDp->buffer[1] = I1; + MDp->buffer[2] = I2; + MDp->buffer[3] = I3; + for (i=0;i<8;i++) MDp->count[i] = 0; + MDp->done = 0; +} + +/* MDblock(MDp,X) +** Update message digest buffer MDp->buffer using 16-word data block X. +** Assumes all 16 words of X are full of data. +** Does not update MDp->count. +** This routine is not user-callable. +*/ +static void +MDblock(MDp,Xb) +MD4_CTX *MDp; +unsigned char *Xb; +{ + register unsigned int tmp, A, B, C, D; + unsigned int X[16]; + int i; + + for (i = 0; i < 16; ++i) { + X[i] = Xb[0] + (Xb[1] << 8) + (Xb[2] << 16) + (Xb[3] << 24); + Xb += 4; + } + + A = MDp->buffer[0]; + B = MDp->buffer[1]; + C = MDp->buffer[2]; + D = MDp->buffer[3]; + /* Update the message digest buffer */ + ff(A , B , C , D , 0 , fs1); /* Round 1 */ + ff(D , A , B , C , 1 , fs2); + ff(C , D , A , B , 2 , fs3); + ff(B , C , D , A , 3 , fs4); + ff(A , B , C , D , 4 , fs1); + ff(D , A , B , C , 5 , fs2); + ff(C , D , A , B , 6 , fs3); + ff(B , C , D , A , 7 , fs4); + ff(A , B , C , D , 8 , fs1); + ff(D , A , B , C , 9 , fs2); + ff(C , D , A , B , 10 , fs3); + ff(B , C , D , A , 11 , fs4); + ff(A , B , C , D , 12 , fs1); + ff(D , A , B , C , 13 , fs2); + ff(C , D , A , B , 14 , fs3); + ff(B , C , D , A , 15 , fs4); + gg(A , B , C , D , 0 , gs1); /* Round 2 */ + gg(D , A , B , C , 4 , gs2); + gg(C , D , A , B , 8 , gs3); + gg(B , C , D , A , 12 , gs4); + gg(A , B , C , D , 1 , gs1); + gg(D , A , B , C , 5 , gs2); + gg(C , D , A , B , 9 , gs3); + gg(B , C , D , A , 13 , gs4); + gg(A , B , C , D , 2 , gs1); + gg(D , A , B , C , 6 , gs2); + gg(C , D , A , B , 10 , gs3); + gg(B , C , D , A , 14 , gs4); + gg(A , B , C , D , 3 , gs1); + gg(D , A , B , C , 7 , gs2); + gg(C , D , A , B , 11 , gs3); + gg(B , C , D , A , 15 , gs4); + hh(A , B , C , D , 0 , hs1); /* Round 3 */ + hh(D , A , B , C , 8 , hs2); + hh(C , D , A , B , 4 , hs3); + hh(B , C , D , A , 12 , hs4); + hh(A , B , C , D , 2 , hs1); + hh(D , A , B , C , 10 , hs2); + hh(C , D , A , B , 6 , hs3); + hh(B , C , D , A , 14 , hs4); + hh(A , B , C , D , 1 , hs1); + hh(D , A , B , C , 9 , hs2); + hh(C , D , A , B , 5 , hs3); + hh(B , C , D , A , 13 , hs4); + hh(A , B , C , D , 3 , hs1); + hh(D , A , B , C , 11 , hs2); + hh(C , D , A , B , 7 , hs3); + hh(B , C , D , A , 15 , hs4); + MDp->buffer[0] += A; + MDp->buffer[1] += B; + MDp->buffer[2] += C; + MDp->buffer[3] += D; +} + +/* MD4Update(MDp,X,count) +** Input: X -- a pointer to an array of unsigned characters. +** count -- the number of bits of X to use. +** (if not a multiple of 8, uses high bits of last byte.) +** Update MDp using the number of bits of X given by count. +** This is the basic input routine for an MD4 user. +** The routine completes the MD computation when count < 512, so +** every MD computation should end with one call to MD4Update with a +** count less than 512. A call with count 0 will be ignored if the +** MD has already been terminated (done != 0), so an extra call with +** count 0 can be given as a "courtesy close" to force termination +** if desired. +*/ +void +MD4Update(MDp,X,count) +MD4_CTX *MDp; +unsigned char *X; +unsigned int count; +{ + unsigned int i, tmp, bit, byte, mask; + unsigned char XX[64]; + unsigned char *p; + + /* return with no error if this is a courtesy close with count + ** zero and MDp->done is true. + */ + if (count == 0 && MDp->done) return; + /* check to see if MD is already done and report error */ + if (MDp->done) + { printf("\nError: MD4Update MD already done."); return; } + + /* Add count to MDp->count */ + tmp = count; + p = MDp->count; + while (tmp) + { tmp += *p; + *p++ = tmp; + tmp = tmp >> 8; + } + + /* Process data */ + if (count == 512) + { /* Full block of data to handle */ + MDblock(MDp,X); + } + else if (count > 512) /* Check for count too large */ + { + printf("\nError: MD4Update called with illegal count value %d.", + count); + return; + } + else /* partial block -- must be last block so finish up */ + { + /* Find out how many bytes and residual bits there are */ + byte = count >> 3; + bit = count & 7; + /* Copy X into XX since we need to modify it */ + for (i=0;i<=byte;i++) XX[i] = X[i]; + for (i=byte+1;i<64;i++) XX[i] = 0; + /* Add padding '1' bit and low-order zeros in last byte */ + mask = 1 << (7 - bit); + XX[byte] = (XX[byte] | mask) & ~( mask - 1); + /* If room for bit count, finish up with this block */ + if (byte <= 55) + { + for (i=0;i<8;i++) XX[56+i] = MDp->count[i]; + MDblock(MDp,XX); + } + else /* need to do two blocks to finish up */ + { + MDblock(MDp,XX); + for (i=0;i<56;i++) XX[i] = 0; + for (i=0;i<8;i++) XX[56+i] = MDp->count[i]; + MDblock(MDp,XX); + } + /* Set flag saying we're done with MD computation */ + MDp->done = 1; + } +} + +/* +** Finish up MD4 computation and return message digest. +*/ +void +MD4Final(buf, MD) +unsigned char *buf; +MD4_CTX *MD; +{ + int i, j; + unsigned int w; + + MD4Update(MD, NULL, 0); + for (i = 0; i < 4; ++i) { + w = MD->buffer[i]; + for (j = 0; j < 4; ++j) { + *buf++ = w; + w >>= 8; + } + } +} + +/* +** End of md4.c +****************************(cut)***********************************/ diff --git a/cpukit/pppd/md4.h b/cpukit/pppd/md4.h new file mode 100644 index 0000000000..80e8f9a2ac --- /dev/null +++ b/cpukit/pppd/md4.h @@ -0,0 +1,64 @@ + +/* +** ******************************************************************** +** md4.h -- Header file for implementation of ** +** MD4 Message Digest Algorithm ** +** Updated: 2/13/90 by Ronald L. Rivest ** +** (C) 1990 RSA Data Security, Inc. ** +** ******************************************************************** +*/ + +#ifndef __P +# if defined(__STDC__) || defined(__GNUC__) +# define __P(x) x +# else +# define __P(x) () +# endif +#endif + + +/* MDstruct is the data structure for a message digest computation. +*/ +typedef struct { + unsigned int buffer[4]; /* Holds 4-word result of MD computation */ + unsigned char count[8]; /* Number of bits processed so far */ + unsigned int done; /* Nonzero means MD computation finished */ +} MD4_CTX; + +/* MD4Init(MD4_CTX *) +** Initialize the MD4_CTX prepatory to doing a message digest +** computation. +*/ +extern void MD4Init __P((MD4_CTX *MD)); + +/* MD4Update(MD,X,count) +** Input: X -- a pointer to an array of unsigned characters. +** count -- the number of bits of X to use (an unsigned int). +** Updates MD using the first "count" bits of X. +** The array pointed to by X is not modified. +** If count is not a multiple of 8, MD4Update uses high bits of +** last byte. +** This is the basic input routine for a user. +** The routine terminates the MD computation when count < 512, so +** every MD computation should end with one call to MD4Update with a +** count less than 512. Zero is OK for a count. +*/ +extern void MD4Update __P((MD4_CTX *MD, unsigned char *X, unsigned int count)); + +/* MD4Print(MD) +** Prints message digest buffer MD as 32 hexadecimal digits. +** Order is from low-order byte of buffer[0] to high-order byte +** of buffer[3]. +** Each byte is printed with high-order hexadecimal digit first. +*/ +extern void MD4Print __P((MD4_CTX *)); + +/* MD4Final(buf, MD) +** Returns message digest from MD and terminates the message +** digest computation. +*/ +extern void MD4Final __P((unsigned char *, MD4_CTX *)); + +/* +** End of md4.h +****************************(cut)***********************************/ diff --git a/cpukit/pppd/md5.c b/cpukit/pppd/md5.c new file mode 100644 index 0000000000..db48023c5b --- /dev/null +++ b/cpukit/pppd/md5.c @@ -0,0 +1,306 @@ + + +/* + *********************************************************************** + ** md5.c -- the source code for MD5 routines ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#include "md5.h" + +/* + *********************************************************************** + ** Message-digest routines: ** + ** To form the message digest for a message M ** + ** (1) Initialize a context buffer mdContext using MD5Init ** + ** (2) Call MD5Update on mdContext and M ** + ** (3) Call MD5Final on mdContext ** + ** The message digest is now in mdContext->digest[0...15] ** + *********************************************************************** + */ + +/* forward declaration */ +static void Transform (); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G, H and I are basic MD5 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +#ifdef __STDC__ +#define UL(x) x##U +#else +#define UL(x) x +#endif + +/* The routine MD5Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void MD5Init (mdContext) +MD5_CTX *mdContext; +{ + mdContext->i[0] = mdContext->i[1] = (UINT4)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (UINT4)0x67452301; + mdContext->buf[1] = (UINT4)0xefcdab89; + mdContext->buf[2] = (UINT4)0x98badcfe; + mdContext->buf[3] = (UINT4)0x10325476; +} + +/* The routine MD5Update updates the message-digest context to + account for the presence of each of the characters inBuf[0..inLen-1] + in the message whose digest is being computed. + */ +void MD5Update (mdContext, inBuf, inLen) +MD5_CTX *mdContext; +unsigned char *inBuf; +unsigned int inLen; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((UINT4)inLen << 3); + mdContext->i[1] += ((UINT4)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +/* The routine MD5Final terminates the message-digest computation and + ends with the desired message digest in mdContext->digest[0...15]. + */ +void MD5Final (hash, mdContext) +unsigned char hash[]; +MD5_CTX *mdContext; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } + memcpy(hash, mdContext->digest, 16); +} + +/* Basic MD5 step. Transforms buf based on in. + */ +static void Transform (buf, in) +UINT4 *buf; +UINT4 *in; +{ + UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */ + FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */ + FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */ + FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */ + FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */ + FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */ + FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */ + GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */ + GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */ + GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */ + GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */ + GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */ + GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */ + HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */ + HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */ + HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */ + HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */ + HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */ + HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */ + II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */ + II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */ + II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */ + II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */ + II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */ + II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */ + II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */ + II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */ + II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */ + II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */ + II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */ + II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */ + II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */ + II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */ + II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + *********************************************************************** + ** End of md5.c ** + ******************************** (cut) ******************************** + */ diff --git a/cpukit/pppd/md5.h b/cpukit/pppd/md5.h new file mode 100644 index 0000000000..7492b2228e --- /dev/null +++ b/cpukit/pppd/md5.h @@ -0,0 +1,58 @@ +/* + *********************************************************************** + ** md5.h -- header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#ifndef __MD5_INCLUDE__ + +/* typedef a 32-bit type */ +typedef unsigned int UINT4; + +/* Data structure for MD5 (Message-Digest) computation */ +typedef struct { + UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ + UINT4 buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init (); +void MD5Update (); +void MD5Final (); + +#define __MD5_INCLUDE__ +#endif /* __MD5_INCLUDE__ */ diff --git a/cpukit/pppd/options.c b/cpukit/pppd/options.c new file mode 100644 index 0000000000..6eafb89eca --- /dev/null +++ b/cpukit/pppd/options.c @@ -0,0 +1,393 @@ +/* + * options.c - handles option processing for PPP. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +/* static char rcsid[] = "$Id$"; */ +#endif + +#include +#include +#include +#include +#include +/* #include */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef PPP_FILTER +#include +#include /* XXX: To get struct pcap */ +#endif + +#include "pppd.h" +#include "pathnames.h" +#include "patchlevel.h" +#include "fsm.h" +#include "lcp.h" +#include "ipcp.h" +#include "upap.h" +#include "chap.h" +#include "ccp.h" +#ifdef CBCP_SUPPORT +#include "cbcp.h" +#endif + +#ifdef IPX_CHANGE +#include "ipxcp.h" +#endif /* IPX_CHANGE */ + +#include + +#define FALSE 0 +#define TRUE 1 + + +#ifndef GIDSET_TYPE +#define GIDSET_TYPE gid_t +#endif +#if 0 +static int privileged_option; /* set iff the current option came from root */ +static char *option_source; /* string saying where the option came from */ +#endif + +/* + * Option variables and default values. + */ +#ifdef PPP_FILTER +int dflag = 0; /* Tell libpcap we want debugging */ +#endif +int debug = 1; /* Debug flag */ +int kdebugflag = 1; /* Tell kernel to print debug messages */ +int default_device = 1; /* Using /dev/tty or equivalent */ +char devnam[MAXPATHLEN] = "/dev/sccppp"; /* Device name */ +int crtscts = 0; /* Use hardware flow control */ +int modem = 0; /* Use modem control lines */ +int inspeed = B115200; /* Input/Output speed requested */ +u_int32_t netmask = 0; /* IP netmask to set on interface */ +int lockflag = 0; /* Create lock file to lock the serial dev */ +int nodetach = 0; /* Don't detach from controlling tty */ +char *connector[]={"TIMEOUT","3","ABORT","\nBUSY\r","ABORT","\nNO DIALTONE\r","ABORT","\nNO CARRIER\r","ABORT","\nNO ANSWER\r","ABORT","\nRINGING\r\n\r\nRINGING\r", + "","\rAT","OK-+++\\c-OK","ATH0","TIMEOUT","30","OK","ATDT13","CONNECT",""}; +/*char *connector[]={"TIMEOUT","3","ABORT","\nBUSY\r","ABORT","\nNO DIALTONE\r","ABORT","\nNO CARRIER\r","ABORT","\nNO ANSWER\r","ABORT","\nRINGING\r\n\r\nRINGING\r", + "","\rAT","OK-+++\\c-OK","ATH0","TIMEOUT","30","OK","ATDT0202122","CONNECT","","ppp","","Username:","ppp","Password:","ppp"}; +*/ +char *disconnector []; /* Script to disestablish physical link */ +char *welcomer []; /* Script to run after phys link estab. */ +int maxconnect = 0; /* Maximum connect time */ +char user[MAXNAMELEN]="stb"; /* Username for PAP */ +char passwd[MAXSECRETLEN]="stb"; /* Password for PAP */ +int auth_required = 0; /* Peer is required to authenticate */ +int defaultroute = 1; /* assign default route through interface */ +int proxyarp = 0; /* Set up proxy ARP entry for peer */ +int persist = 1; /* Reopen link after it goes down */ +int uselogin = 0; /* Use /etc/passwd for checking PAP */ +int lcp_echo_interval = 0; /* Interval between LCP echo-requests */ +int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */ +char our_name[MAXNAMELEN]="infotel"; /* Our name for authentication purposes */ +char remote_name[MAXNAMELEN]; /* Peer's name for authentication */ +int explicit_remote = 0; /* User specified explicit remote name */ +int usehostname = 0; /* Use hostname for our_name */ +int disable_defaultip = 1; /* Don't use hostname for default IP adrs */ +int demand = 0; /* do dial-on-demand */ +char *ipparam = NULL; /* Extra parameter for ip up/down scripts */ +int cryptpap; /* Passwords in pap-secrets are encrypted */ +int idle_time_limit = 0; /* Disconnect if idle for this many seconds */ +int holdoff = 30; /* # seconds to pause before reconnecting */ +int refuse_pap = 0; /* Set to say we won't do PAP */ +int refuse_chap = 1; /* Set to say we won't do CHAP */ + +#ifdef MSLANMAN +int ms_lanman = 0; /* Nonzero if use LanMan password instead of NT */ + /* Has meaning only with MS-CHAP challenges */ +#endif + +struct option_info auth_req_info; +struct option_info connector_info; +struct option_info disconnector_info; +struct option_info welcomer_info; +struct option_info devnam_info; +#ifdef PPP_FILTER +struct bpf_program pass_filter;/* Filter program for packets to pass */ +struct bpf_program active_filter; /* Filter program for link-active pkts */ +pcap_t pc; /* Fake struct pcap so we can compile expr */ +#endif + +/* + * Prototypes + */ +#if 0 +static int setdevname __P((char *, int)); +static int setipaddr __P((char *)); +static int setspeed __P((char *)); +static int setdebug __P((char **)); +static int setkdebug __P((char **)); +static int setpassive __P((char **)); +static int setsilent __P((char **)); +static int noopt __P((char **)); +static int setnovj __P((char **)); +static int setnovjccomp __P((char **)); +static int setvjslots __P((char **)); +static int reqpap __P((char **)); +static int nopap __P((char **)); +#ifdef OLD_OPTIONS +static int setupapfile __P((char **)); +#endif +static int nochap __P((char **)); +static int reqchap __P((char **)); +static int noaccomp __P((char **)); +static int noasyncmap __P((char **)); +static int noip __P((char **)); +static int nomagicnumber __P((char **)); +static int setasyncmap __P((char **)); +static int setescape __P((char **)); +static int setmru __P((char **)); +static int setmtu __P((char **)); +#ifdef CBCP_SUPPORT +static int setcbcp __P((char **)); +#endif +static int nomru __P((char **)); +static int nopcomp __P((char **)); +static int setconnector __P((char **)); +static int setdisconnector __P((char **)); +static int setwelcomer __P((char **)); +static int setmaxconnect __P((char **)); +static int setdomain __P((char **)); +static int setnetmask __P((char **)); +static int setcrtscts __P((char **)); +static int setnocrtscts __P((char **)); +static int setxonxoff __P((char **)); +static int setnodetach __P((char **)); +static int setupdetach __P((char **)); +static int setmodem __P((char **)); +static int setlocal __P((char **)); +static int setlock __P((char **)); +static int setname __P((char **)); +static int setuser __P((char **)); +static int setremote __P((char **)); +static int setauth __P((char **)); +static int setnoauth __P((char **)); +static int readfile __P((char **)); +static int callfile __P((char **)); +static int setdefaultroute __P((char **)); +static int setnodefaultroute __P((char **)); +static int setproxyarp __P((char **)); +static int setnoproxyarp __P((char **)); +static int setpersist __P((char **)); +static int setnopersist __P((char **)); +static int setdologin __P((char **)); +static int setusehostname __P((char **)); +static int setnoipdflt __P((char **)); +static int setlcptimeout __P((char **)); +static int setlcpterm __P((char **)); +static int setlcpconf __P((char **)); +static int setlcpfails __P((char **)); +static int setipcptimeout __P((char **)); +static int setipcpterm __P((char **)); +static int setipcpconf __P((char **)); +static int setipcpfails __P((char **)); +static int setpaptimeout __P((char **)); +static int setpapreqs __P((char **)); +static int setpapreqtime __P((char **)); +static int setchaptimeout __P((char **)); +static int setchapchal __P((char **)); +static int setchapintv __P((char **)); +static int setipcpaccl __P((char **)); +static int setipcpaccr __P((char **)); +static int setlcpechointv __P((char **)); +static int setlcpechofails __P((char **)); +static int noccp __P((char **)); +static int setbsdcomp __P((char **)); +static int setnobsdcomp __P((char **)); +static int setdeflate __P((char **)); +static int setnodeflate __P((char **)); +static int setnodeflatedraft __P((char **)); +static int setdemand __P((char **)); +static int setpred1comp __P((char **)); +static int setnopred1comp __P((char **)); +static int setipparam __P((char **)); +static int setpapcrypt __P((char **)); +static int setidle __P((char **)); +static int setholdoff __P((char **)); +static int setdnsaddr __P((char **)); +static int resetipxproto __P((char **)); +static int setwinsaddr __P((char **)); +static int showversion __P((char **)); +static int showhelp __P((char **)); + +#ifdef PPP_FILTER +static int setpdebug __P((char **)); +static int setpassfilter __P((char **)); +static int setactivefilter __P((char **)); +#endif + +#ifdef IPX_CHANGE +static int setipxproto __P((char **)); +static int setipxanet __P((char **)); +static int setipxalcl __P((char **)); +static int setipxarmt __P((char **)); +static int setipxnetwork __P((char **)); +static int setipxnode __P((char **)); +static int setipxrouter __P((char **)); +static int setipxname __P((char **)); +static int setipxcptimeout __P((char **)); +static int setipxcpterm __P((char **)); +static int setipxcpconf __P((char **)); +static int setipxcpfails __P((char **)); +#endif /* IPX_CHANGE */ + +#ifdef MSLANMAN +static int setmslanman __P((char **)); +#endif + +static int number_option __P((char *, u_int32_t *, int)); +static int int_option __P((char *, int *)); +static int readable __P((int fd)); +#endif + +/* + * Valid arguments. + */ + +/* + * parse_args - parse a string of arguments from the command line. + */ +int +parse_args(argc, argv) + int argc; + char **argv; +{ + + +return 0; +} + +/* + * scan_args - scan the command line arguments to get the tty name, + * if specified. + */ + +/* + * usage - print out a message telling how to use the program. + */ +void +usage() +{ + } + +/* + * showhelp - print out usage message and exit. + */ +static int +showhelp(argv) + char **argv; +{ + return 0; +} + +/* + * showversion - print out the version number and exit. + */ +static int +showversion(argv) + char **argv; +{ + return 0; +} + +void +option_error __V((char *fmt, ...)) +{ +} +/* + * readable - check if a file is readable by the real user. + */ +/*static int +readable(fd) + int fd; +{ + uid_t uid; + int ngroups, i; + struct stat sbuf; + GIDSET_TYPE groups[NGROUPS_MAX]; + + uid = getuid(); + if (uid == 0) + return 1; + if (fstat(fd, &sbuf) != 0) + return 0; + if (sbuf.st_uid == uid) + return sbuf.st_mode & S_IRUSR; + if (sbuf.st_gid == getgid()) + return sbuf.st_mode & S_IRGRP; + ngroups = getgroups(NGROUPS_MAX, groups); + for (i = 0; i < ngroups; ++i) + if (sbuf.st_gid == groups[i]) + return sbuf.st_mode & S_IRGRP; + return sbuf.st_mode & S_IROTH; +} +*/ +/* + * Read a word from a file. + * Words are delimited by white-space or by quotes (" or '). + * Quotes, white-space and \ may be escaped with \. + * \ is ignored. + */ + + /* + * The following procedures parse options. + */ + +/* + * readfile - take commands from a file. + */ + +/* + * callfile - take commands from /etc/ppp/peers/. + * Name may not contain /../, start with / or ../, or end in /.. + */ + /* + * setdebug - Set debug (command line argument). + */ + + +/* + * noopt - Disable all options. + */ +static int +noopt(argv) + char **argv; +{ + BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options)); + BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options)); + BZERO((char *) &ipcp_wantoptions[0], sizeof (struct ipcp_options)); + BZERO((char *) &ipcp_allowoptions[0], sizeof (struct ipcp_options)); + +#ifdef IPX_CHANGE + BZERO((char *) &ipxcp_wantoptions[0], sizeof (struct ipxcp_options)); + BZERO((char *) &ipxcp_allowoptions[0], sizeof (struct ipxcp_options)); +#endif /* IPX_CHANGE */ + + return (1); +} diff --git a/cpukit/pppd/patchlevel.h b/cpukit/pppd/patchlevel.h new file mode 100644 index 0000000000..02bf115c52 --- /dev/null +++ b/cpukit/pppd/patchlevel.h @@ -0,0 +1,6 @@ +/* $Id$ */ +#define PATCHLEVEL 5 + +#define VERSION "2.3" +#define IMPLEMENTATION "" +#define DATE "4 May 1998" diff --git a/cpukit/pppd/pathnames.h b/cpukit/pppd/pathnames.h new file mode 100644 index 0000000000..b954204f5c --- /dev/null +++ b/cpukit/pppd/pathnames.h @@ -0,0 +1,30 @@ +/* + * define path names + * + * $Id$ + */ + +#ifdef HAVE_PATHS_H +#include + +#else +#define _PATH_VARRUN "/etc/ppp/" +#define _PATH_DEVNULL "/dev/null" +#endif + +#define _PATH_UPAPFILE "/etc/ppp/pap-secrets" +#define _PATH_CHAPFILE "/etc/ppp/chap-secrets" +#define _PATH_SYSOPTIONS "/etc/ppp/options" +#define _PATH_IPUP "/etc/ppp/ip-up" +#define _PATH_IPDOWN "/etc/ppp/ip-down" +#define _PATH_AUTHUP "/etc/ppp/auth-up" +#define _PATH_AUTHDOWN "/etc/ppp/auth-down" +#define _PATH_TTYOPT "/etc/ppp/options." +#define _PATH_CONNERRS "/etc/ppp/connect-errors" +#define _PATH_USEROPT ".ppprc" +#define _PATH_PEERFILES "/etc/ppp/peers/" + +#ifdef IPX_CHANGE +#define _PATH_IPXUP "/etc/ppp/ipx-up" +#define _PATH_IPXDOWN "/etc/ppp/ipx-down" +#endif /* IPX_CHANGE */ diff --git a/cpukit/pppd/pppd.h b/cpukit/pppd/pppd.h new file mode 100644 index 0000000000..7c12e38ae5 --- /dev/null +++ b/cpukit/pppd/pppd.h @@ -0,0 +1,497 @@ +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * TODO: + */ + +#ifndef __PPPD_H__ +#define __PPPD_H__ + +#include /* for FILE */ +#include /* for MAXPATHLEN and BSD4_4, if defined */ +#include /* for u_int32_t, if defined */ +#include /* for struct timeval */ +#include + +#if __STDC__ +#include +#define __V(x) x +#else +#include +#define __V(x) (va_alist) va_dcl +#define const +#endif + +/* + * Limits. + */ + +#define NUM_PPP 1 /* One PPP interface supported (per process) */ +#define MAXWORDLEN 1024 /* max length of word in file (incl null) */ +#define MAXARGS 1 /* max # args to a command */ +#define MAXNAMELEN 256 /* max length of hostname or name for auth */ +#define MAXSECRETLEN 256 /* max length of password or secret */ + +/* + * Global variables. + */ + +extern int hungup; /* Physical layer has disconnected */ +extern int interfunit; /* Interface unit number */ +extern char ifname[]; /* Interface name */ +extern int ttyfd; /* Serial device file descriptor */ +extern char hostname[]; /* Our hostname */ +extern u_char outpacket_buf[]; /* Buffer for outgoing packets */ +extern int phase; /* Current state of link - see values below */ +extern int baud_rate; /* Current link speed in bits/sec */ +extern char *progname; /* Name of this program */ +extern int redirect_stderr;/* Connector's stderr should go to file */ +extern char peer_authname[];/* Authenticated name of peer */ +extern int privileged; /* We were run by real-uid root */ +extern int need_holdoff; /* Need holdoff period after link terminates */ +extern char **script_env; /* Environment variables for scripts */ +extern int detached; /* Have detached from controlling tty */ + +/* + * Variables set by command-line options. + */ + +extern int debug; /* Debug flag */ +extern int kdebugflag; /* Tell kernel to print debug messages */ +extern int default_device; /* Using /dev/tty or equivalent */ +extern char devnam[]; /* Device name */ +extern int crtscts; /* Use hardware flow control */ +extern int modem; /* Use modem control lines */ +extern int inspeed; /* Input/Output speed requested */ +extern u_int32_t netmask; /* IP netmask to set on interface */ +extern int lockflag; /* Create lock file to lock the serial dev */ +extern int nodetach; /* Don't detach from controlling tty */ +extern char *connector[]; /* Script to establish physical link */ +extern char *disconnector[]; /* Script to disestablish physical link */ +extern char *welcomer[]; /* Script to welcome client after connection */ +extern int maxconnect; /* Maximum connect time (seconds) */ +extern char user[]; /* Our name for authenticating ourselves */ +extern char passwd[]; /* Password for PAP */ +extern int auth_required; /* Peer is required to authenticate */ +extern int proxyarp; /* Set up proxy ARP entry for peer */ +extern int persist; /* Reopen link after it goes down */ +extern int uselogin; /* Use /etc/passwd for checking PAP */ +extern int lcp_echo_interval; /* Interval between LCP echo-requests */ +extern int lcp_echo_fails; /* Tolerance to unanswered echo-requests */ +extern char our_name[]; /* Our name for authentication purposes */ +extern char remote_name[]; /* Peer's name for authentication */ +extern int explicit_remote;/* remote_name specified with remotename opt */ +extern int usehostname; /* Use hostname for our_name */ +extern int disable_defaultip; /* Don't use hostname for default IP adrs */ +extern int demand; /* Do dial-on-demand */ +extern char *ipparam; /* Extra parameter for ip up/down scripts */ +extern int cryptpap; /* Others' PAP passwords are encrypted */ +extern int idle_time_limit;/* Shut down link if idle for this long */ +extern int holdoff; /* Dead time before restarting */ +extern int refuse_pap; /* Don't wanna auth. ourselves with PAP */ +extern int refuse_chap; /* Don't wanna auth. ourselves with CHAP */ +#ifdef PPP_FILTER +extern struct bpf_program pass_filter; /* Filter for pkts to pass */ +extern struct bpf_program active_filter; /* Filter for link-active pkts */ +#endif + + +#ifdef MSLANMAN +extern int ms_lanman; /* Nonzero if use LanMan password instead of NT */ + /* Has meaning only with MS-CHAP challenges */ +#endif + +/* + * Values for phase. + */ +#define PHASE_DEAD 0 +#define PHASE_INITIALIZE 1 +#define PHASE_DORMANT 2 +#define PHASE_ESTABLISH 3 +#define PHASE_AUTHENTICATE 4 +#define PHASE_CALLBACK 5 +#define PHASE_NETWORK 6 +#define PHASE_TERMINATE 7 +#define PHASE_HOLDOFF 8 + +/* + * The following struct gives the addresses of procedures to call + * for a particular protocol. + */ +struct protent { + u_short protocol; /* PPP protocol number */ + /* Initialization procedure */ + void (*init) __P((int unit)); + /* Process a received packet */ + void (*input) __P((int unit, u_char *pkt, int len)); + /* Process a received protocol-reject */ + void (*protrej) __P((int unit)); + /* Lower layer has come up */ + void (*lowerup) __P((int unit)); + /* Lower layer has gone down */ + void (*lowerdown) __P((int unit)); + /* Open the protocol */ + void (*open) __P((int unit)); + /* Close the protocol */ + void (*close) __P((int unit, char *reason)); + /* Print a packet in readable form */ + int (*printpkt) __P((u_char *pkt, int len, + void (*printer) __P((void *, char *, ...)), + void *arg)); + /* Process a received data packet */ + void (*datainput) __P((int unit, u_char *pkt, int len)); + int enabled_flag; /* 0 iff protocol is disabled */ + char *name; /* Text name of protocol */ + /* Check requested options, assign defaults */ + void (*check_options) __P((void)); + /* Configure interface for demand-dial */ + int (*demand_conf) __P((int unit)); + /* Say whether to bring up link for this pkt */ + int (*active_pkt) __P((u_char *pkt, int len)); +}; + +/* Table of pointers to supported protocols */ +extern struct protent *protocols[]; + +/* + * Prototypes. + */ + +/* Procedures exported from main.c. */ +void detach __P((void)); /* Detach from controlling tty */ +void die __P((int)); /* Cleanup and exit */ +void quit __P((void)); /* like die(1) */ +void novm __P((char *)); /* Say we ran out of memory, and die */ +void my_timeout __P((void (*func)(void *), void *arg, int t)); + /* Call func(arg) after t seconds */ +void untimeout __P((void (*func)(void *), void *arg)); + /* Cancel call to func(arg) */ +int run_program __P((char *prog, char **args, int must_exist)); + /* Run program prog with args in child */ +void demuxprotrej __P((int, int)); + /* Demultiplex a Protocol-Reject */ +void format_packet __P((u_char *, int, void (*) (void *, char *, ...), + void *)); /* Format a packet in human-readable form */ +void log_packet __P((u_char *, int, char *, int)); + /* Format a packet and log it with syslog */ +void print_string __P((char *, int, void (*) (void *, char *, ...), + void *)); /* Format a string for output */ +int fmtmsg __P((char *, int, char *, ...)); /* sprintf++ */ +int vfmtmsg __P((char *, int, char *, va_list)); /* vsprintf++ */ +void script_setenv __P((char *, char *)); /* set script env var */ +void script_unsetenv __P((char *)); /* unset script env var */ +/* My procedures */ +int connect_stb(); +int disconnect_stb(); +int pppdmain(int, char*[]); +int chatmain(char*); + +/* Procedures exported from auth.c */ + +void link_required __P((int)); /* we are starting to use the link */ +void link_terminated __P((int)); /* we are finished with the link */ +void link_down __P((int)); /* the LCP layer has left the Opened state */ +void link_established __P((int)); /* the link is up; authenticate now */ +void np_up __P((int, int)); /* a network protocol has come up */ +void np_down __P((int, int)); /* a network protocol has gone down */ +void np_finished __P((int, int)); /* a network protocol no longer needs link */ +void auth_peer_fail __P((int, int)); + /* peer failed to authenticate itself */ +void auth_peer_success __P((int, int, char *, int)); + /* peer successfully authenticated itself */ +void auth_withpeer_fail __P((int, int)); + /* we failed to authenticate ourselves */ +void auth_withpeer_success __P((int, int)); + /* we successfully authenticated ourselves */ +void auth_check_options __P((void)); + /* check authentication options supplied */ +void auth_reset __P((int)); /* check what secrets we have */ +int check_passwd __P((int, char *, int, char *, int, char **, int *)); + /* Check peer-supplied username/password */ +int get_secret __P((int, char *, char *, char *, int *, int)); + /* get "secret" for chap */ +int auth_ip_addr __P((int, u_int32_t)); + /* check if IP address is authorized */ +int bad_ip_adrs __P((u_int32_t)); + /* check if IP address is unreasonable */ +void check_access __P((FILE *, char *)); + /* check permissions on secrets file */ + +/* Procedures exported from demand.c */ +void demand_conf __P((void)); /* config interface(s) for demand-dial */ +void demand_block __P((void)); /* set all NPs to queue up packets */ +void demand_unblock __P((void)); /* set all NPs to pass packets */ +void demand_discard __P((void)); /* set all NPs to discard packets */ +void demand_rexmit __P((int)); /* retransmit saved frames for an NP */ +int loop_chars __P((unsigned char *, int)); /* process chars from loopback */ +int loop_frame __P((unsigned char *, int)); /* process frame from loopback */ + +/* Procedures exported from sys-*.c */ +void sys_init __P((void)); /* Do system-dependent initialization */ +void sys_cleanup __P((void)); /* Restore system state before exiting */ +void sys_check_options __P((void)); /* Check options specified */ +void sys_close __P((void)); /* Clean up in a child before execing */ +int ppp_available __P((void)); /* Test whether ppp kernel support exists */ +void open_ppp_loopback __P((void)); /* Open loopback for demand-dialling */ +void establish_ppp __P((int)); /* Turn serial port into a ppp interface */ +void restore_loop __P((void)); /* Transfer ppp unit back to loopback */ +void disestablish_ppp __P((int)); /* Restore port to normal operation */ +void clean_check __P((void)); /* Check if line was 8-bit clean */ +void set_up_tty __P((int, int)); /* Set up port's speed, parameters, etc. */ +void restore_tty __P((int)); /* Restore port's original parameters */ +void setdtr __P((int, int)); /* Raise or lower port's DTR line */ +void output __P((int, u_char *, int)); /* Output a PPP packet */ +void wait_input __P((struct timeval *)); + /* Wait for input, with timeout */ +void wait_loop_output __P((struct timeval *)); + /* Wait for pkt from loopback, with timeout */ +void wait_time __P((struct timeval *)); /* Wait for given length of time */ +int read_packet __P((u_char *)); /* Read PPP packet */ +int get_loop_output __P((void)); /* Read pkts from loopback */ +void ppp_send_config __P((int, int, u_int32_t, int, int)); + /* Configure i/f transmit parameters */ +void ppp_set_xaccm __P((int, ext_accm)); + /* Set extended transmit ACCM */ +void ppp_recv_config __P((int, int, u_int32_t, int, int)); + /* Configure i/f receive parameters */ +int ccp_test __P((int, u_char *, int, int)); + /* Test support for compression scheme */ +void ccp_flags_set __P((int, int, int)); + /* Set kernel CCP state */ +int ccp_fatal_error __P((int)); /* Test for fatal decomp error in kernel */ +int get_idle_time __P((int, struct ppp_idle *)); + /* Find out how long link has been idle */ +int sifvjcomp __P((int, int, int, int)); + /* Configure VJ TCP header compression */ +int sifup __P((int)); /* Configure i/f up (for IP) */ +int sifnpmode __P((int u, int proto, enum NPmode mode)); + /* Set mode for handling packets for proto */ +int sifdown __P((int)); /* Configure i/f down (for IP) */ +int sifaddr __P((int, u_int32_t, u_int32_t, u_int32_t)); + /* Configure IP addresses for i/f */ +int cifaddr __P((int, u_int32_t, u_int32_t)); + /* Reset i/f IP addresses */ +int sifdefaultroute __P((int, u_int32_t, u_int32_t)); + /* Create default route through i/f */ +int cifdefaultroute __P((int, u_int32_t, u_int32_t)); + /* Delete default route through i/f */ +int sifproxyarp __P((int, u_int32_t)); + /* Add proxy ARP entry for peer */ +int cifproxyarp __P((int, u_int32_t)); + /* Delete proxy ARP entry for peer */ +u_int32_t GetMask __P((u_int32_t)); /* Get appropriate netmask for address */ +int lock __P((char *)); /* Create lock file for device */ +void unlock __P((void)); /* Delete previously-created lock file */ +int daemon __P((int, int)); /* Detach us from terminal session */ +void logwtmp __P((const char *, const char *, const char *)); + /* Write entry to wtmp file */ +int get_host_seed __P((void)); /* Get host-dependent random number seed */ +#ifdef PPP_FILTER +int set_filters __P((struct bpf_program *pass, struct bpf_program *active)); + /* Set filter programs in kernel */ +#endif + +/* Procedures exported from options.c */ +int parse_args __P((int argc, char **argv)); + /* Parse options from arguments given */ +void usage __P((void)); /* Print a usage message */ +int options_from_file __P((char *filename, int must_exist, int check_prot, + int privileged)); + /* Parse options from an options file */ +int options_from_user __P((void)); /* Parse options from user's .ppprc */ +int options_for_tty __P((void)); /* Parse options from /etc/ppp/options.tty */ +void scan_args __P((int argc, char **argv)); + /* Look for tty name in command-line args */ +int getword __P((FILE *f, char *word, int *newlinep, char *filename)); + /* Read a word from a file */ +void option_error __P((char *fmt, ...)); + /* Print an error message about an option */ + +/* + * This structure is used to store information about certain + * options, such as where the option value came from (/etc/ppp/options, + * command line, etc.) and whether it came from a privileged source. + */ + +struct option_info { + int priv; /* was value set by sysadmin? */ + char *source; /* where option came from */ +}; + +extern struct option_info auth_req_info; +extern struct option_info connector_info; +extern struct option_info disconnector_info; +extern struct option_info welcomer_info; +extern struct option_info devnam_info; + +/* + * Inline versions of get/put char/short/long. + * Pointer is advanced; we assume that both arguments + * are lvalues and will already be in registers. + * cp MUST be u_char *. + */ +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (u_char) (c); \ +} + + +#define GETSHORT(s, cp) { \ + (s) = *(cp)++ << 8; \ + (s) |= *(cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u_char) ((s) >> 8); \ + *(cp)++ = (u_char) (s); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp)++ << 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (u_char) ((l) >> 24); \ + *(cp)++ = (u_char) ((l) >> 16); \ + *(cp)++ = (u_char) ((l) >> 8); \ + *(cp)++ = (u_char) (l); \ +} + +#define INCPTR(n, cp) ((cp) += (n)) +#define DECPTR(n, cp) ((cp) -= (n)) + +#undef FALSE +#define FALSE 0 +#undef TRUE +#define TRUE 1 + +/* + * System dependent definitions for user-level 4.3BSD UNIX implementation. + */ + +#define DEMUXPROTREJ(u, p) demuxprotrej(u, p) + +#define TIMEOUT(r, f, t) my_timeout((r), (f), (t)) +#define UNTIMEOUT(r, f) untimeout((r), (f)) + +#define BCOPY(s, d, l) memcpy(d, s, l) +#define BZERO(s, n) memset(s, 0, n) +#define EXIT(u) quit() + +#define PRINTMSG(m, l) { m[l] = '\0'; syslog(LOG_INFO, "Remote message: %s", m); } + +/* + * MAKEHEADER - Add Header fields to a packet. + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(PPP_ALLSTATIONS, p); \ + PUTCHAR(PPP_UI, p); \ + PUTSHORT(t, p); } + +/* #define DEBUGALL */ + +#ifdef DEBUGALL +#define DEBUGMAIN 1 +#define DEBUGFSM 1 +#define DEBUGLCP 1 +#define DEBUGIPCP 1 +#define DEBUGUPAP 1 +#define DEBUGCHAP 1 +#endif + +#ifndef LOG_PPP /* we use LOG_LOCAL2 for syslog by default */ +#if defined(DEBUGMAIN) || defined(DEBUGFSM) || defined(DEBUGSYS) \ + || defined(DEBUGLCP) || defined(DEBUGIPCP) || defined(DEBUGUPAP) \ + || defined(DEBUGCHAP) || defined(DEBUG) +#define LOG_PPP LOG_LOCAL2 +#else +#define LOG_PPP LOG_DAEMON +#endif +#endif /* LOG_PPP */ +extern rtems_id pppdaemon_tid; +#ifdef DEBUGMAIN +#define MAINDEBUG(x) if (debug) syslog x +#else +#define MAINDEBUG(x) +#endif + +#ifdef DEBUGSYS +#define SYSDEBUG(x) if (debug) syslog x +#else +#define SYSDEBUG(x) +#endif + +#ifdef DEBUGFSM +#define FSMDEBUG(x) if (debug) syslog x +#else +#define FSMDEBUG(x) +#endif + +#ifdef DEBUGLCP +#define LCPDEBUG(x) if (debug) syslog x +#else +#define LCPDEBUG(x) +#endif + +#ifdef DEBUGIPCP +#define IPCPDEBUG(x) if (debug) syslog x +#else +#define IPCPDEBUG(x) +#endif + +#ifdef DEBUGUPAP +#define UPAPDEBUG(x) if (debug) syslog x +#else +#define UPAPDEBUG(x) +#endif + +#ifdef DEBUGCHAP +#define CHAPDEBUG(x) if (debug) syslog x +#else +#define CHAPDEBUG(x) +#endif + +#ifdef DEBUGIPXCP +#define IPXCPDEBUG(x) if (debug) syslog x +#else +#define IPXCPDEBUG(x) +#endif + +#ifndef SIGTYPE +#if defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) +#define SIGTYPE void +#else +#define SIGTYPE int +#endif /* defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) */ +#endif /* SIGTYPE */ + +#ifndef MIN +#define MIN(a, b) ((a) < (b)? (a): (b)) +#endif +#ifndef MAX +#define MAX(a, b) ((a) > (b)? (a): (b)) +#endif + +#endif /* __PPP_H__ */ diff --git a/cpukit/pppd/upap.c b/cpukit/pppd/upap.c new file mode 100644 index 0000000000..f2cdb921c5 --- /dev/null +++ b/cpukit/pppd/upap.c @@ -0,0 +1,619 @@ +/* + * upap.c - User/Password Authentication Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +/* static char rcsid[] = "$Id$"; */ +#endif + +/* + * TODO: + */ + +#include +#include +#include +#include +#include + +#include "pppd.h" +#include "upap.h" +#define print_string(user, ulen, printer, arg) + +/* + * Protocol entry points. + */ +static void upap_init __P((int)); +static void upap_lowerup __P((int)); +static void upap_lowerdown __P((int)); +static void upap_input __P((int, u_char *, int)); +static void upap_protrej __P((int)); +static int upap_printpkt __P((u_char *, int, + void (*) __P((void *, char *, ...)), void *)); + +struct protent pap_protent = { + PPP_PAP, + upap_init, + upap_input, + upap_protrej, + upap_lowerup, + upap_lowerdown, + NULL, + NULL, + upap_printpkt, + NULL, + 1, + "PAP", + NULL, + NULL, + NULL +}; + +upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */ + +static void upap_timeout __P((void *)); +static void upap_reqtimeout __P((void *)); +static void upap_rauthreq __P((upap_state *, u_char *, int, int)); +static void upap_rauthack __P((upap_state *, u_char *, int, int)); +static void upap_rauthnak __P((upap_state *, u_char *, int, int)); +static void upap_sauthreq __P((upap_state *)); +static void upap_sresp __P((upap_state *, int, int, char *, int)); + + +/* + * upap_init - Initialize a UPAP unit. + */ +static void +upap_init(unit) + int unit; +{ + upap_state *u = &upap[unit]; + + u->us_unit = unit; + u->us_user = NULL; + u->us_userlen = 0; + u->us_passwd = NULL; + u->us_passwdlen = 0; + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; + u->us_id = 0; + u->us_timeouttime = UPAP_DEFTIMEOUT; + u->us_maxtransmits = 10; + u->us_reqtimeout = UPAP_DEFREQTIME; +} + + +/* + * upap_authwithpeer - Authenticate us with our peer (start client). + * + * Set new state and send authenticate's. + */ +void +upap_authwithpeer(unit, user, password) + int unit; + char *user, *password; +{ + upap_state *u = &upap[unit]; + + /* Save the username and password we're given */ + u->us_user = user; + u->us_userlen = strlen(user); + u->us_passwd = password; + u->us_passwdlen = strlen(password); + u->us_transmits = 0; + + /* Lower layer up yet? */ + if (u->us_clientstate == UPAPCS_INITIAL || + u->us_clientstate == UPAPCS_PENDING) { + u->us_clientstate = UPAPCS_PENDING; + return; + } + + upap_sauthreq(u); /* Start protocol */ +} + + +/* + * upap_authpeer - Authenticate our peer (start server). + * + * Set new state. + */ +void +upap_authpeer(unit) + int unit; +{ + upap_state *u = &upap[unit]; + + /* Lower layer up yet? */ + if (u->us_serverstate == UPAPSS_INITIAL || + u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_PENDING; + return; + } + + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); +} + + +/* + * upap_timeout - Retransmission timer for sending auth-reqs expired. + */ +static void +upap_timeout(arg) + void *arg; +{ + upap_state *u = (upap_state *) arg; + + if (u->us_clientstate != UPAPCS_AUTHREQ) + return; + + if (u->us_transmits >= u->us_maxtransmits) { + /* give up in disgust */ + syslog(LOG_ERR, "No response to PAP authenticate-requests"); + u->us_clientstate = UPAPCS_BADAUTH; + auth_withpeer_fail(u->us_unit, PPP_PAP); + return; + } + + upap_sauthreq(u); /* Send Authenticate-Request */ +} + + +/* + * upap_reqtimeout - Give up waiting for the peer to send an auth-req. + */ +static void +upap_reqtimeout(arg) + void *arg; +{ + upap_state *u = (upap_state *) arg; + + if (u->us_serverstate != UPAPSS_LISTEN) + return; /* huh?? */ + + auth_peer_fail(u->us_unit, PPP_PAP); + u->us_serverstate = UPAPSS_BADAUTH; +} + + +/* + * upap_lowerup - The lower layer is up. + * + * Start authenticating if pending. + */ +static void +upap_lowerup(unit) + int unit; +{ + upap_state *u = &upap[unit]; + + if (u->us_clientstate == UPAPCS_INITIAL) + u->us_clientstate = UPAPCS_CLOSED; + else if (u->us_clientstate == UPAPCS_PENDING) { + upap_sauthreq(u); /* send an auth-request */ + } + + if (u->us_serverstate == UPAPSS_INITIAL) + u->us_serverstate = UPAPSS_CLOSED; + else if (u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + } +} + + +/* + * upap_lowerdown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +upap_lowerdown(unit) + int unit; +{ + upap_state *u = &upap[unit]; + + if (u->us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */ + UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ + if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) + UNTIMEOUT(upap_reqtimeout, u); + + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; +} + + +/* + * upap_protrej - Peer doesn't speak this protocol. + * + * This shouldn't happen. In any case, pretend lower layer went down. + */ +static void +upap_protrej(unit) + int unit; +{ + upap_state *u = &upap[unit]; + + if (u->us_clientstate == UPAPCS_AUTHREQ) { + syslog(LOG_ERR, "PAP authentication failed due to protocol-reject"); + auth_withpeer_fail(unit, PPP_PAP); + } + if (u->us_serverstate == UPAPSS_LISTEN) { + syslog(LOG_ERR, "PAP authentication of peer failed (protocol-reject)"); + auth_peer_fail(unit, PPP_PAP); + } + upap_lowerdown(unit); +} + + +/* + * upap_input - Input UPAP packet. + */ +static void +upap_input(unit, inpacket, l) + int unit; + u_char *inpacket; + int l; +{ + upap_state *u = &upap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < UPAP_HEADERLEN) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd short header.")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < UPAP_HEADERLEN) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd illegal length.")); + return; + } + if (len > l) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd short packet.")); + return; + } + len -= UPAP_HEADERLEN; + + /* + * Action depends on code. + */ + switch (code) { + case UPAP_AUTHREQ: + upap_rauthreq(u, inp, id, len); + break; + + case UPAP_AUTHACK: + upap_rauthack(u, inp, id, len); + break; + + case UPAP_AUTHNAK: + upap_rauthnak(u, inp, id, len); + break; + + default: /* XXX Need code reject */ + break; + } +} + + +/* + * upap_rauth - Receive Authenticate. + */ +static void +upap_rauthreq(u, inp, id, len) + upap_state *u; + u_char *inp; + int id; + int len; +{ + u_char ruserlen, rpasswdlen; + char *ruser, *rpasswd; + int retcode; + char *msg; + int msglen; + + UPAPDEBUG((LOG_INFO, "pap_rauth: Rcvd id %d.", id)); + + if (u->us_serverstate < UPAPSS_LISTEN) + return; + + /* + * If we receive a duplicate authenticate-request, we are + * supposed to return the same status as for the first request. + */ + if (u->us_serverstate == UPAPSS_OPEN) { + upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ + return; + } + if (u->us_serverstate == UPAPSS_BADAUTH) { + upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ + return; + } + + /* + * Parse user/passwd. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.")); + return; + } + GETCHAR(ruserlen, inp); + len -= sizeof (u_char) + ruserlen + sizeof (u_char); + if (len < 0) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.")); + return; + } + ruser = (char *) inp; + INCPTR(ruserlen, inp); + GETCHAR(rpasswdlen, inp); + if (len < rpasswdlen) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.")); + return; + } + rpasswd = (char *) inp; + + /* + * Check the username and password given. + */ + retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, + rpasswdlen, &msg, &msglen); + BZERO(rpasswd, rpasswdlen); + + upap_sresp(u, retcode, id, msg, msglen); + + if (retcode == UPAP_AUTHACK) { + u->us_serverstate = UPAPSS_OPEN; + auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen); + } else { + u->us_serverstate = UPAPSS_BADAUTH; + auth_peer_fail(u->us_unit, PPP_PAP); + } + + if (u->us_reqtimeout > 0) + UNTIMEOUT(upap_reqtimeout, u); +} + + +/* + * upap_rauthack - Receive Authenticate-Ack. + */ +static void +upap_rauthack(u, inp, id, len) + upap_state *u; + u_char *inp; + int id; + int len; +{ + u_char msglen; + char *msg; + + UPAPDEBUG((LOG_INFO, "pap_rauthack: Rcvd id %d.", id)); + if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */ + return; + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet.")); + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet.")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + + u->us_clientstate = UPAPCS_OPEN; + + auth_withpeer_success(u->us_unit, PPP_PAP); +} + + +/* + * upap_rauthnak - Receive Authenticate-Nakk. + */ +static void +upap_rauthnak(u, inp, id, len) + upap_state *u; + u_char *inp; + int id; + int len; +{ + u_char msglen; + char *msg; + + UPAPDEBUG((LOG_INFO, "pap_rauthnak: Rcvd id %d.", id)); + if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */ + return; + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet.")); + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet.")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + + u->us_clientstate = UPAPCS_BADAUTH; + + syslog(LOG_ERR, "PAP authentication failed"); + auth_withpeer_fail(u->us_unit, PPP_PAP); +} + + +/* + * upap_sauthreq - Send an Authenticate-Request. + */ +static void +upap_sauthreq(u) + upap_state *u; +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + + u->us_userlen + u->us_passwdlen; + outp = outpacket_buf; + + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(UPAP_AUTHREQ, outp); + PUTCHAR(++u->us_id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(u->us_userlen, outp); + BCOPY(u->us_user, outp, u->us_userlen); + INCPTR(u->us_userlen, outp); + PUTCHAR(u->us_passwdlen, outp); + BCOPY(u->us_passwd, outp, u->us_passwdlen); + + output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN); + + UPAPDEBUG((LOG_INFO, "pap_sauth: Sent id %d.", u->us_id)); + + TIMEOUT(upap_timeout, u, u->us_timeouttime); + ++u->us_transmits; + u->us_clientstate = UPAPCS_AUTHREQ; +} + + +/* + * upap_sresp - Send a response (ack or nak). + */ +static void +upap_sresp(u, code, id, msg, msglen) + upap_state *u; + u_char code, id; + char *msg; + int msglen; +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; + outp = outpacket_buf; + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(msglen, outp); + BCOPY(msg, outp, msglen); + output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN); + + UPAPDEBUG((LOG_INFO, "pap_sresp: Sent code %d, id %d.", code, id)); +} + +/* + * upap_printpkt - print the contents of a PAP packet. + */ +static char *upap_codenames[] = { + "AuthReq", "AuthAck", "AuthNak" +}; + +static int +upap_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int code, id, len; + int mlen, ulen, wlen; + char *user, *pwd, *msg; + u_char *pstart; + + if (plen < UPAP_HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < UPAP_HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(upap_codenames) / sizeof(char *)) + printer(arg, " %s", upap_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= UPAP_HEADERLEN; + switch (code) { + case UPAP_AUTHREQ: + if (len < 1) + break; + ulen = p[0]; + if (len < ulen + 2) + break; + wlen = p[ulen + 1]; + if (len < ulen + wlen + 2) + break; + user = (char *) (p + 1); + pwd = (char *) (p + ulen + 2); + p += ulen + wlen + 2; + len -= ulen + wlen + 2; + printer(arg, " user="); + print_string(user, ulen, printer, arg); + printer(arg, " password="); + print_string(pwd, wlen, printer, arg); + break; + case UPAP_AUTHACK: + case UPAP_AUTHNAK: + if (len < 1) + break; + mlen = p[0]; + if (len < mlen + 1) + break; + msg = (char *) (p + 1); + p += mlen + 1; + len -= mlen + 1; + printer(arg, " "); + print_string(msg, mlen, printer, arg); + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} diff --git a/cpukit/pppd/upap.h b/cpukit/pppd/upap.h new file mode 100644 index 0000000000..510efa3151 --- /dev/null +++ b/cpukit/pppd/upap.h @@ -0,0 +1,87 @@ +/* + * upap.h - User/Password Authentication Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + */ + +/* + * Packet header = Code, id, length. + */ +#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * UPAP codes. + */ +#define UPAP_AUTHREQ 1 /* Authenticate-Request */ +#define UPAP_AUTHACK 2 /* Authenticate-Ack */ +#define UPAP_AUTHNAK 3 /* Authenticate-Nak */ + + +/* + * Each interface is described by upap structure. + */ +typedef struct upap_state { + int us_unit; /* Interface unit number */ + char *us_user; /* User */ + int us_userlen; /* User length */ + char *us_passwd; /* Password */ + int us_passwdlen; /* Password length */ + int us_clientstate; /* Client state */ + int us_serverstate; /* Server state */ + u_char us_id; /* Current id */ + int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */ + int us_transmits; /* Number of auth-reqs sent */ + int us_maxtransmits; /* Maximum number of auth-reqs to send */ + int us_reqtimeout; /* Time to wait for auth-req from peer */ +} upap_state; + + +/* + * Client states. + */ +#define UPAPCS_INITIAL 0 /* Connection down */ +#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPCS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */ +#define UPAPCS_OPEN 4 /* We've received an Ack */ +#define UPAPCS_BADAUTH 5 /* We've received a Nak */ + +/* + * Server states. + */ +#define UPAPSS_INITIAL 0 /* Connection down */ +#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPSS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */ +#define UPAPSS_OPEN 4 /* We've sent an Ack */ +#define UPAPSS_BADAUTH 5 /* We've sent a Nak */ + + +/* + * Timeouts. + */ +#define UPAP_DEFTIMEOUT 3 /* Timeout (seconds) for retransmitting req */ +#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ + +extern upap_state upap[]; + +void upap_authwithpeer __P((int, char *, char *)); +void upap_authpeer __P((int)); + +extern struct protent pap_protent; -- cgit v1.2.3