pppd/Makefile.linux | 2 +- pppd/auth.c | 9 +- pppd/cbcp.c | 504 ++++++++++++++++++++++++++++++++++++++++++++++----- pppd/cbcp.h | 14 ++ pppd/ipcp.c | 9 + pppd/ipv6cp.c | 10 + pppd/ipxcp.c | 10 + pppd/lcp.c | 10 + pppd/main.c | 94 ++++++++++ pppd/options.c | 36 ++++ pppd/pathnames.h | 7 + pppd/pppd.8 | 8 + pppd/pppd.h | 12 ++ pppd/sys-linux.c | 2 +- pppd/tty.c | 188 ++++++++++++-------- 15 files changed, 790 insertions(+), 125 deletions(-) diff --git a/pppd/Makefile.linux b/pppd/Makefile.linux index 060db6a..59de73b 100644 --- a/pppd/Makefile.linux +++ b/pppd/Makefile.linux @@ -17,7 +17,7 @@ PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap-new.c md5.c ccp.c \ HEADERS = ccp.h session.h chap-new.h ecp.h fsm.h ipcp.h \ ipxcp.h lcp.h magic.h md5.h patchlevel.h pathnames.h pppd.h \ - upap.h eap.h + upap.h eap.h cbcp.h MANPAGES = pppd.8 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap-new.o md5.o ccp.o \ diff --git a/pppd/auth.c b/pppd/auth.c index fb71944..7a7a70b 100644 --- a/pppd/auth.c +++ b/pppd/auth.c @@ -245,7 +245,7 @@ extern char *crypt __P((const char *, const char *)); /* Prototypes for procedures local to this file. */ -static void network_phase __P((int)); +void network_phase __P((int)); static void check_idle __P((void *)); static void connect_time_expired __P((void *)); static int null_login __P((int)); @@ -591,8 +591,10 @@ void start_link(unit) notice("Starting negotiation on %s", ppp_devnam); add_fd(fd_ppp); +#ifndef CBCP_SUPPORT status = EXIT_NEGOTIATION_FAILED; new_phase(PHASE_ESTABLISH); +#endif lcp_lowerup(0); return; @@ -804,7 +806,7 @@ link_established(unit) /* * Proceed to the network phase. */ -static void +void network_phase(unit) int unit; { @@ -830,7 +832,8 @@ network_phase(unit) /* * If we negotiated callback, do it now. */ - if (go->neg_cbcp) { + if (((go->neg_cbcp) || (lcp_hisoptions[unit].neg_cbcp)) + && (phase != PHASE_CALLBACK)) { new_phase(PHASE_CALLBACK); (*cbcp_protent.open)(unit); return; diff --git a/pppd/cbcp.c b/pppd/cbcp.c index 7f2f787..3c1f366 100644 --- a/pppd/cbcp.c +++ b/pppd/cbcp.c @@ -44,19 +44,24 @@ #include "cbcp.h" #include "fsm.h" #include "lcp.h" +#include "ipcp.h" +#include "pathnames.h" +#include static const char rcsid[] = RCSID; /* * Options. */ -static int setcbcp __P((char **)); +/* +int setcbcp __P((char **)); static option_t cbcp_option_list[] = { { "callback", o_special, (void *)setcbcp, "Ask for callback", OPT_PRIO | OPT_A2STRVAL, &cbcp[0].us_number }, { NULL } }; +*/ /* * Protocol entry points. @@ -84,7 +89,7 @@ struct protent cbcp_protent = { 0, "CBCP", NULL, - cbcp_option_list, + NULL, NULL, NULL, NULL @@ -94,26 +99,22 @@ cbcp_state cbcp[NUM_PPP]; /* internal prototypes */ +static void cbcp_sendreq __P((void *arg)); static void cbcp_recvreq __P((cbcp_state *us, u_char *pckt, int len)); -static void cbcp_resp __P((cbcp_state *us)); -static void cbcp_up __P((cbcp_state *us)); +static void cbcp_sendresp __P((cbcp_state *us)); +static void cbcp_recvresp __P((cbcp_state *us, char *pckt, int len)); +static void cbcp_sendack __P((void *)); static void cbcp_recvack __P((cbcp_state *us, u_char *pckt, int len)); static void cbcp_send __P((cbcp_state *us, int code, u_char *buf, int len)); +static void cbcp_make_options __P((int unit)); +static int cbcp_check_user __P((char *user, char *mask)); +static void cbcp_start_callback __P((cbcp_state *us)); +static void cbcp_up __P((cbcp_state *us)); -/* option processing */ -static int -setcbcp(argv) - char **argv; -{ - lcp_wantoptions[0].neg_cbcp = 1; - cbcp_protent.enabled_flag = 1; - cbcp[0].us_number = strdup(*argv); - if (cbcp[0].us_number == 0) - novm("callback number"); - cbcp[0].us_type |= (1 << CB_CONF_USER); - cbcp[0].us_type |= (1 << CB_CONF_ADMIN); - return (1); -} +cbcp_state *stop_iface = NULL; + +void (*cbcp_init_hook) __P((cbcp_state *)) = NULL; + /* init state */ static void @@ -125,7 +126,6 @@ cbcp_init(iface) us = &cbcp[iface]; memset(us, 0, sizeof(cbcp_state)); us->us_unit = iface; - us->us_type |= (1 << CB_CONF_NO); } /* lower layer is up */ @@ -137,9 +137,6 @@ cbcp_lowerup(iface) dbglog("cbcp_lowerup"); dbglog("want: %d", us->us_type); - - if (us->us_type == CB_CONF_USER) - dbglog("phone no: %s", us->us_number); } static void @@ -147,6 +144,8 @@ cbcp_open(unit) int unit; { dbglog("cbcp_open"); + if (lcp_hisoptions[unit].neg_cbcp) + cbcp_make_options(unit); } /* process an incomming packet */ @@ -161,6 +160,8 @@ cbcp_input(unit, inpacket, pktlen) u_short len; cbcp_state *us = &cbcp[unit]; + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *his = &lcp_hisoptions[unit]; inp = inpacket; @@ -184,16 +185,34 @@ cbcp_input(unit, inpacket, pktlen) switch(code) { case CBCP_REQ: + if ( !go->neg_cbcp ) + { + dbglog("CBCP received CBCP_REQ, but CBCP running in server mode!"); + return; + } us->us_id = id; cbcp_recvreq(us, inp, len); break; case CBCP_RESP: - if (debug) - dbglog("CBCP_RESP received"); + if ( !his->neg_cbcp ) + { + dbglog("CBCP received CBCP_RESP, but CBCP running in client mode!"); + return; + } + if (id != us->us_id) + dbglog("id doesn't match: expected %d recv %d", + us->us_id, id); + + cbcp_recvresp(us, inp, len); break; case CBCP_ACK: + if ( !go->neg_cbcp ) + { + dbglog("CBCP received CBCP_ACK, but CBCP running in server mode!"); + return; + } if (debug && id != us->us_id) dbglog("id doesn't match: expected %d recv %d", us->us_id, id); @@ -275,7 +294,7 @@ cbcp_printpkt(p, plen, printer, arg) printer(arg, " delay = %d", delay); } - if (olen > 3) { + if (olen > 4) { int addrt; char str[256]; @@ -307,7 +326,7 @@ cbcp_recvreq(us, pckt, pcktlen) u_char *pckt; int pcktlen; { - u_char type, opt_len, delay, addr_type; + u_char type, opt_len, addr_type; char address[256]; int len = pcktlen; @@ -321,8 +340,9 @@ cbcp_recvreq(us, pckt, pcktlen) if (opt_len < 2 || opt_len > len) break; + us->us_delay = 0; if (opt_len > 2) - GETCHAR(delay, pckt); + GETCHAR(us->us_delay, pckt); us->us_allowed |= (1 << type); @@ -357,28 +377,29 @@ cbcp_recvreq(us, pckt, pcktlen) return; } - cbcp_resp(us); + cbcp_sendresp(us); } static void -cbcp_resp(us) +cbcp_sendresp(us) cbcp_state *us; { - u_char cb_type; + u_char cb_allowed; u_char buf[256]; u_char *bufp = buf; int len = 0; int slen; - cb_type = us->us_allowed & us->us_type; - dbglog("cbcp_resp cb_type=%d", cb_type); + cb_allowed = us->us_allowed; + dbglog("cbcp_resp cb_type=%d", cb_allowed); #if 0 if (!cb_type) lcp_down(us->us_unit); #endif - if (cb_type & ( 1 << CB_CONF_USER ) ) { + if (cb_allowed & ( 1 << CB_CONF_USER ) ) { + us->us_type = 1 << CB_CONF_USER; dbglog("cbcp_resp CONF_USER"); slen = strlen(us->us_number); if (slen > 250) { @@ -387,31 +408,33 @@ cbcp_resp(us) } PUTCHAR(CB_CONF_USER, bufp); len = 3 + 1 + slen + 1; - PUTCHAR(len , bufp); - PUTCHAR(5, bufp); /* delay */ + PUTCHAR(len, bufp); + PUTCHAR(us->us_delay, bufp); /* delay */ PUTCHAR(1, bufp); BCOPY(us->us_number, bufp, slen + 1); cbcp_send(us, CBCP_RESP, buf, len); return; } - if (cb_type & ( 1 << CB_CONF_ADMIN ) ) { + if (cb_allowed & ( 1 << CB_CONF_ADMIN ) ) { + us->us_type = 1 << CB_CONF_ADMIN; dbglog("cbcp_resp CONF_ADMIN"); PUTCHAR(CB_CONF_ADMIN, bufp); len = 3; PUTCHAR(len, bufp); - PUTCHAR(5, bufp); /* delay */ + PUTCHAR(us->us_delay, bufp); /* delay */ cbcp_send(us, CBCP_RESP, buf, len); return; } - if (cb_type & ( 1 << CB_CONF_NO ) ) { + if (cb_allowed & ( 1 << CB_CONF_NO ) ) { + us->us_type = 1 << CB_CONF_NO; dbglog("cbcp_resp CONF_NO"); PUTCHAR(CB_CONF_NO, bufp); len = 2; PUTCHAR(len , bufp); cbcp_send(us, CBCP_RESP, buf, len); - start_networks(us->us_unit); +// start_networks(us->us_unit); return; } } @@ -448,17 +471,19 @@ cbcp_recvack(us, pckt, len) u_char *pckt; int len; { - u_char type, delay, addr_type; + u_char type, addr_type; int opt_len; char address[256]; + stop_iface = us; + if (len >= 2) { GETCHAR(type, pckt); GETCHAR(opt_len, pckt); if (opt_len >= 2 && opt_len <= len) { if (opt_len > 2) - GETCHAR(delay, pckt); + GETCHAR(us->us_delay, pckt); if (opt_len > 4) { GETCHAR(addr_type, pckt); @@ -467,22 +492,407 @@ cbcp_recvack(us, pckt, len) if (address[0]) dbglog("peer will call: %s", address); } - if (type == CB_CONF_NO) - return; - - cbcp_up(us); + if (type != CB_CONF_NO) + { + callback_in_progress = us->us_unit + 1; + callback_in_progress |= CBCP_CLIENT; + cbcp_up(us); + } + else + network_phase(us->us_unit); } else if (debug) dbglog("cbcp_recvack: malformed packet"); } } -/* ok peer will do callback */ -static void +/* Make options + if auth req, options from callback-users file, else use CBCP_CONF_USER */ + static void +cbcp_make_options (unit) + int unit; +{ + cbcp_state *us = &cbcp[unit]; + FILE *userfile; + struct stat sbuf; + int best_fit, got_fit, newline; + char uname[ 256 ], option[ 256 ]; + + us->us_id = 1; + us->us_count = 0; + us->us_delay = 5; /* Default delay 5 seconds */ + if ( *peer_authname ) { /* Username available */ + userfile = fopen( _PATH_CBCP_USERS, "r" ); + if ( userfile == NULL ){ + dbglog( "Can't open callback user file: %s %m", + _PATH_CBCP_USERS ); + dbglog( "Allow user definied callback." ); + us->us_allowed = ( 1 << CB_CONF_USER ); + }else + { + if ( fstat(fileno(userfile), &sbuf) < 0) { + dbglog("Cannot stat userfile file %s: %m", + _PATH_CBCP_USERS ); + } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) { + dbglog("Warning - user file %s has world and/or group access", + _PATH_CBCP_USERS ); + } + + us->us_allowed = ( 1 << CB_CONF_NO ); /* Assume, no callback allowed */ + + if (getword(userfile, uname, &newline, _PATH_CBCP_USERS)){ /* file not empty */ + newline = 1; + best_fit = 0; + *option = 0; + for (;;) { + /* + * Skip until we find a word at the start of a line. + */ + while (!newline && getword(userfile, uname, + &newline, _PATH_CBCP_USERS)) + ; + if (!newline) + break; /* got to end of file */ + + /* + * Got a user - check if it's a match or a wildcard. + */ + got_fit = cbcp_check_user( peer_authname, uname ); + if ( got_fit <= best_fit ){ + newline = 0; + continue; + } + + /* Read the options */ + best_fit = got_fit; + if (getword(userfile, option, &newline, _PATH_CBCP_USERS)) + break; + + if ( newline ) + *option = 0; + + if ( best_fit == 100 ) + break; + } + } + + switch ( *option ){ + case '-' : us->us_allowed = ( 1 << CB_CONF_NO ); break; + case '*' : + case 0 : us->us_allowed = ( 1 << CB_CONF_USER ); break; + default : us->us_allowed = ( 1 << CB_CONF_ADMIN ); + us->us_number = strdup( option ); break; + } + fclose( userfile ); + } + } + else + us->us_allowed = ( 1 << CB_CONF_USER ); + + /* We always accept no callback users */ + us->us_allowed |= ( 1 << CB_CONF_NO ); + + if (cbcp_init_hook) + cbcp_init_hook( us ); + + cbcp_sendreq( us ); +} + + +/* make cbcp request packet & send it */ + static void +cbcp_sendreq (arg) + void *arg; +{ + cbcp_state *us=(cbcp_state *)arg; + u_char cb_allow = us->us_allowed; + u_char buf[256]; + u_char *bufp = buf; + int len = 0; + + us->us_count++; + if (us->us_count<=CBCP_MAXRETRY) + TIMEOUT( cbcp_sendreq, arg, CBCP_DEFTIMEOUT ); + else + { + lcp_close(0, "Sorry, CBCP not responding."); + return; + } + dbglog("cbcp_sendreq cb_allowed=%d", cb_allow); + + + if (cb_allow & ( 1 << CB_CONF_USER ) ) { + dbglog("cbcp_sendreq CONF_USER"); + PUTCHAR(CB_CONF_USER, bufp); + len+=3; + PUTCHAR(3 , bufp); + PUTCHAR(us->us_delay, bufp); + } + + if (cb_allow & ( 1 << CB_CONF_ADMIN ) ) { + dbglog("cbcp_sendreq CONF_ADMIN"); + PUTCHAR(CB_CONF_ADMIN, bufp); + len += 3; + PUTCHAR(3 , bufp); + PUTCHAR(us->us_delay, bufp); + } + + if (cb_allow & ( 1 << CB_CONF_NO ) ) { + dbglog("cbcp_sendreq CONF_NO"); + PUTCHAR(CB_CONF_NO, bufp); + len += 3; + PUTCHAR(3 , bufp); + PUTCHAR(us->us_delay, bufp); + } + + if (len) + cbcp_send(us, CBCP_REQ, buf, len); + else + { + dbglog("cbcp: no available options to client!"); + } +} + +/* Received CBCP response, make ACK */ + static void +cbcp_recvresp (us, pckt, len) + cbcp_state *us; + char *pckt; + int len; +{ + u_char type, addr_type; + int opt_len; + char address[256]; + + if (len) { + GETCHAR(type, pckt); + GETCHAR(opt_len, pckt); + + if (!(( 1 << type )& us->us_allowed )) { + dbglog("CBCP received options not allowed on server!"); + return; + } + + if ((type!= CB_CONF_NO ) && + (type!= CB_CONF_USER ) && + (type!= CB_CONF_ADMIN )) { + dbglog("CBCP received BAD Response: too more or unknown options %d",type); + return; + } + + UNTIMEOUT( cbcp_sendreq, us ); + us->us_count = 0; + + + if (opt_len > 2) + GETCHAR(us->us_delay, pckt) + if ( us->us_delay < 5 ) + us->us_delay = 5; + + if (opt_len > 4) { + GETCHAR(addr_type, pckt); /* Address Type mezo elvesztve !!! */ + memcpy(address, pckt, opt_len - 4); + address[opt_len - 4] = 0; + if (address[0]) + dbglog("peer will callback the client on: %s", address); + us->us_number=strdup( address ); + } + + us->us_type = ( 1 << type ); + cbcp_sendack( us ); + } + else + { + dbglog("CBCP received BAD Response: size to small"); + } +} + +/* Send the CBCP_ACK packet */ + static void +cbcp_sendack (arg) + void *arg; +{ + cbcp_state *us= (cbcp_state *)arg; + u_char cb_type; + u_char buf[256]; + u_char *bufp = buf; + int len = 0; + + stop_iface = (cbcp_state *)arg; + cb_type = us->us_type; + + dbglog("cbcp_sendack cb_type=%d", cb_type); + + us->us_count++; + if (us->us_count<=CBCP_MAXRETRY) + TIMEOUT( cbcp_sendack, arg, CBCP_DEFTIMEOUT ); + else + { + lcp_close(0, "Sorry, CBCP not responding."); + return; + } + +#if 0 + if (!cb_type) + lcp_down(us->us_unit); +#endif + + if (cb_type == (1 << CB_CONF_USER )) { + dbglog("cbcp_sendack CONF_USER"); + PUTCHAR(CB_CONF_USER, bufp); + len = 3 + 1 + strlen(us->us_number) + 1; + PUTCHAR(len , bufp); + PUTCHAR(us->us_delay, bufp); /* delay */ + PUTCHAR(1, bufp); /* Elvesztett byte... */ + BCOPY(us->us_number, bufp, strlen(us->us_number) + 1); + cbcp_send(us, CBCP_ACK, buf, len); + /* lcp_close( 2, "Illegal, but required to server..." ); */ + callback_in_progress = us->us_unit + 1; + return; + } + + if (cb_type == (1 << CB_CONF_ADMIN )) { + dbglog("cbcp_sendack CONF_ADMIN"); + PUTCHAR(CB_CONF_ADMIN, bufp); + len = 3; + PUTCHAR(len , bufp); + PUTCHAR(us->us_delay, bufp); /* delay */ + PUTCHAR(0, bufp); + cbcp_send(us, CBCP_ACK, buf, len); + /* lcp_close( 2, "Illegal, but required to server..." ); */ + callback_in_progress = us->us_unit + 1; + return; + } + + if (cb_type == (1 << CB_CONF_NO )) { + dbglog("cbcp_sendack CONF_NO"); + PUTCHAR(CB_CONF_NO, bufp); + len = 2; + PUTCHAR(len , bufp); + PUTCHAR(0, bufp); + cbcp_send(us, CBCP_ACK, buf, len); + if (us->us_count<=1) + network_phase(us->us_unit); + return; + } + + dbglog("CBCP - Bad options in Ack routine."); +} + +/* CBCP coming succesful up */ +void cbcp_stop() +{ + if ( stop_iface && lcp_allowoptions[stop_iface->us_unit].neg_cbcp ) + { + UNTIMEOUT( cbcp_sendack, stop_iface ); + cbcp_start_callback( stop_iface ); + } +} + +/* The server side coming up & client 'ack-ed' */ +void cbcp_start_callback (us) + cbcp_state *us; +{ + lcp_allowoptions[us->us_unit].neg_cbcp=0; + + dbglog("cbcp_start_callback running"); +} + +/* The client side coming up: server allowed the callback */ + static void cbcp_up(us) cbcp_state *us; { persist = 0; status = EXIT_CALLBACK; lcp_close(0, "Call me back, please"); + lcp_wantoptions[us->us_unit].neg_cbcp = 0; + dbglog("cbcp_up called"); + lcp_close(us->us_unit, "Call me back, please"); } + +/* The main module gets the script with parameters to run */ +char *cbcp_get_script() +{ + cbcp_state *us = &cbcp[(callback_in_progress & CBCP_NCLIENT)-1]; + char script[ 256 ]; + + if ( callback_in_progress & CBCP_CLIENT ) + sprintf( script, "%s %d", _PATH_CBCP_CLIENT, us->us_delay ); + else + sprintf( script, "%s %d %s", _PATH_CBCP_SERVER, + us->us_delay, us->us_number ); + + return strdup( script ); +} + +/* give me the hit rate. wild cars '*?' valids */ +int cbcp_check_user ( user, mask ) + char *user; + char *mask; +{ + char *curr_user = user; + char *curr_mask = mask; + char *find, backp = 0; + int count, len = 0; + + if ( !strcasecmp( user, mask )) + return 100; + + if ( !strcmp( mask, "*" )) + return 1; + + if ( !*user ) + return 0; + + count = 0; + + while(( find = strpbrk( curr_mask, "*?" )) != 0 ) { + if ( find != curr_mask ){ + len = find - curr_mask; + if ( strncmp( curr_user, curr_mask, len )) + break; + } + + curr_mask += len + 1; + curr_user += len; + count += len; + if ( *curr_user == 0 ) + break; + + if ( *find == '?' ) { + curr_user++; + if ( *curr_user == 0 ) + break; + } else { + if ( *curr_mask == 0 ) + break; + + if ( ( find = strpbrk( curr_mask, "*?" )) != 0 ){ + backp = *find; + *find = 0; + } + curr_user = strstr( curr_user, curr_mask ); + if ( find ) + *find = backp; + if ( !curr_user ) + break; + + find = strpbrk( curr_mask, "*?" ); + if ( find ) + len = find - curr_mask; + else + len = strlen( curr_mask ); + + curr_mask += len; + curr_user += len; + count += len; + } + } + + if ( *curr_user && *curr_mask && !strcmp( curr_user, curr_mask )) + count += strlen( curr_user ); + + return ( count * 100 / strlen( user ) ); +} + diff --git a/pppd/cbcp.h b/pppd/cbcp.h index c2ab3f6..b47bff7 100644 --- a/pppd/cbcp.h +++ b/pppd/cbcp.h @@ -6,6 +6,8 @@ typedef struct cbcp_state { u_char us_id; /* Current id */ u_char us_allowed; int us_type; + u_char us_delay; + u_char us_count; char *us_number; /* Telefone Number */ } cbcp_state; @@ -19,8 +21,20 @@ extern struct protent cbcp_protent; #define CBCP_RESP 2 #define CBCP_ACK 3 + +#define CBCP_DEFTIMEOUT 5 +#define CBCP_MAXRETRY 50 +#define CBCP_CLIENT 0x8000 +#define CBCP_NCLIENT 0x7fff + + #define CB_CONF_NO 1 #define CB_CONF_USER 2 #define CB_CONF_ADMIN 3 #define CB_CONF_LIST 4 + + +char *cbcp_get_script __P(()); +void cbcp_stop __P(()); + #endif diff --git a/pppd/ipcp.c b/pppd/ipcp.c index 12bcc61..5793c56 100644 --- a/pppd/ipcp.c +++ b/pppd/ipcp.c @@ -61,6 +61,11 @@ #include "ipcp.h" #include "pathnames.h" +#ifdef CBCP_SUPPORT +#include "cbcp.h" +#endif + + static const char rcsid[] = RCSID; /* global vars */ @@ -1418,6 +1423,10 @@ ipcp_reqci(f, inp, len, reject_if_disagree) u_char maxslotindex, cflag; int d; +#ifdef CBCP_SUPPORT + cbcp_stop(); +#endif + /* * Reset all his options. */ diff --git a/pppd/ipv6cp.c b/pppd/ipv6cp.c index 4a09c9a..1627197 100644 --- a/pppd/ipv6cp.c +++ b/pppd/ipv6cp.c @@ -167,6 +167,11 @@ #include "magic.h" #include "pathnames.h" +#ifdef CBCP_SUPPORT +#include "cbcp.h" +#endif + + static const char rcsid[] = RCSID; /* global vars */ @@ -903,6 +908,11 @@ ipv6cp_reqci(f, inp, len, reject_if_disagree) u_char *ucp = inp; /* Pointer to current output char */ int l = *len; /* Length left */ +#ifdef CBCP_SUPPORT + cbcp_stop(); +#endif + + /* * Reset all his options. */ diff --git a/pppd/ipxcp.c b/pppd/ipxcp.c index 7b2343e..9e140d2 100644 --- a/pppd/ipxcp.c +++ b/pppd/ipxcp.c @@ -62,6 +62,11 @@ #include "pathnames.h" #include "magic.h" +#ifdef CBCP_SUPPORT +#include "cbcp.h" +#endif + + static const char rcsid[] = RCSID; /* global vars */ @@ -1003,6 +1008,11 @@ ipxcp_reqci(f, inp, len, reject_if_disagree) u_char *ucp = inp; /* Pointer to current output char */ int l = *len; /* Length left */ +#ifdef CBCP_SUPPORT + cbcp_stop(); +#endif + + /* * Reset all his options. */ diff --git a/pppd/lcp.c b/pppd/lcp.c index 5c77490..68855e2 100644 --- a/pppd/lcp.c +++ b/pppd/lcp.c @@ -1782,6 +1782,16 @@ lcp_reqci(f, inp, lenp, reject_if_disagree) ho->neg_accompression = 1; break; + case CI_CALLBACK: + dbglog("lcp_reqci: rcvd CALLBACK"); + if (!ao->neg_cbcp || + cilen != CILEN_CHAR ) { + orc = CONFREJ; + break; + } + ho->neg_cbcp = 1; + break; + case CI_MRRU: if (!ao->neg_mrru || !multilink || cilen != CILEN_SHORT) { diff --git a/pppd/main.c b/pppd/main.c index 014d614..9e23217 100644 --- a/pppd/main.c +++ b/pppd/main.c @@ -186,6 +186,11 @@ static sigjmp_buf sigjmp; char **script_env; /* Env. variable values for scripts */ int s_env_nalloc; /* # words avail at script_env */ +#ifdef CBCP_SUPPORT +int callback_in_progress; /* Callback running */ +#endif + + u_char outpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for outgoing packet */ u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */ @@ -314,6 +319,9 @@ main(argc, argv) char *p; struct passwd *pw; struct protent *protp; +#ifdef CBCP_SUPPORT + char *connector; +#endif char numbuf[16]; link_stats_valid = 0; @@ -537,6 +545,13 @@ main(argc, argv) lcp_open(0); /* Start protocol */ start_link(0); +#ifdef CBCP_SUPPORT + for(callback_in_progress=1;callback_in_progress;){ + callback_in_progress=0; + + status = EXIT_NEGOTIATION_FAILED; + new_phase(PHASE_ESTABLISH); +#endif while (phase != PHASE_DEAD) { handle_events(); get_input(); @@ -556,6 +571,83 @@ main(argc, argv) } /* restore FSMs to original state */ lcp_close(0, ""); +#ifdef CBCP_SUPPORT + if (callback_in_progress){ + connector = NULL; + cbcp_stop(); + if (fd_ppp != -1) + remove_fd(fd_ppp); + clean_check(); + the_channel->disestablish_ppp(devfd); + fd_ppp = -1; + + if (!hungup){ + lcp_lowerdown(0); + } else { + tty_close_fds(); + setup_serial(connector); + + } + + devfd = callback(); + + /* set up the serial device as a ppp interface */ +#ifdef USE_TDB + tdb_lockall(pppdb); +#endif + fd_ppp = the_channel->establish_ppp(devfd); + if (fd_ppp < 0) { +#ifdef USE_TDB + tdb_unlockall(pppdb); +#endif + status = EXIT_FATAL_ERROR; + asked_to_quit = 1; + break; + } + + if (!demand && ifunit >= 0) + set_ifunit(1); +#ifdef USE_TDB + tdb_unlockall(pppdb); +#endif + + /* + * Start opening the connection and wait for + * incoming events (reply, timeout, etc.). + */ + notice("Connect: %s <--> %s", ifname, ppp_devnam); + gettimeofday(&start_time, NULL); + link_stats_valid = 0; + script_unsetenv("CONNECT_TIME"); + script_unsetenv("BYTES_SENT"); + script_unsetenv("BYTES_RCVD"); + lcp_lowerup(0); + + /* + * If we are initiating this connection, wait for a short + * time for something from the peer. This can avoid bouncing + * our packets off his tty before he has it set up. + */ + add_fd(fd_ppp); + if (listen_time != 0) { + struct timeval t; + t.tv_sec = listen_time / 1000; + t.tv_usec = listen_time % 1000; + wait_input(&t); + } + + /*if (connector != NULL || ptycommand != NULL) { + struct timeval t; + t.tv_sec = 1; + t.tv_usec = 0; + wait_input(&t); + }*/ + + lcp_open(0); /* Start protocol */ + } + } +#endif + if (!persist || asked_to_quit || (maxfail > 0 && unsuccess >= maxfail)) break; @@ -2147,3 +2239,5 @@ cleanup_db() delete_db_key(p); } #endif /* USE_TDB */ + + diff --git a/pppd/options.c b/pppd/options.c index 482eab9..eec148c 100644 --- a/pppd/options.c +++ b/pppd/options.c @@ -75,6 +75,12 @@ #include "pppd.h" #include "pathnames.h" +#ifdef CBCP_SUPPORT +#include "fsm.h" +#include "lcp.h" +#include "cbcp.h" +#endif + #if defined(ultrix) || defined(NeXT) char *strdup __P((char *)); #endif @@ -180,6 +186,11 @@ struct option_list { struct option_list *next; }; +#ifdef CBCP_SUPPORT +static int setcbcp __P((char **)); +#endif + + static struct option_list *extra_options = NULL; /* @@ -321,6 +332,11 @@ option_t general_options[] = { "Check for traffic limit every N seconds", OPT_PRIO | OPT_LLIMIT | 1 }, #endif +#ifdef CBCP_SUPPORT + { "callback", o_special, setcbcp, + "Callback request to server - OR - calling back the client" }, +#endif + { NULL } }; @@ -1623,3 +1639,23 @@ loadplugin(argv) return 0; } #endif /* PLUGIN */ + +#ifdef CBCP_SUPPORT +int +setcbcp(argv) + char **argv; +{ + cbcp[0].us_number = strdup(*argv); + if (cbcp_protent.enabled_flag) + novm("Only one callback parameter supported!"); + if (cbcp[0].us_number == 0) + novm("callback number"); + if (!strcmp(cbcp[0].us_number,"server")){ + lcp_allowoptions[0].neg_cbcp = 1; + } else { + lcp_wantoptions[0].neg_cbcp = 1; + } + cbcp_protent.enabled_flag = 1; + return 1; +} +#endif diff --git a/pppd/pathnames.h b/pppd/pathnames.h index a33f046..5e108c5 100644 --- a/pppd/pathnames.h +++ b/pppd/pathnames.h @@ -45,6 +45,13 @@ #define _PATH_IPXDOWN _ROOT_PATH "/etc/ppp/ipx-down" #endif /* IPX_CHANGE */ +#ifdef CBCP_SUPPORT +#define _PATH_CBCP_SERVER _ROOT_PATH "/etc/ppp/callback-server" +#define _PATH_CBCP_CLIENT _ROOT_PATH "/etc/ppp/callback-client" +#define _PATH_CBCP_USERS _ROOT_PATH "/etc/ppp/callback-users" +#define _PATH_CBCP _ROOT_PATH "/etc/ppp/callback" +#endif /* CBCP_SUPPORT */ + #ifdef __STDC__ #define _PATH_PPPDB _ROOT_PATH _PATH_VARRUN "pppd2.tdb" #else /* __STDC__ */ diff --git a/pppd/pppd.8 b/pppd/pppd.8 index 8ea8200..3cc1e01 100644 --- a/pppd/pppd.8 +++ b/pppd/pppd.8 @@ -88,6 +88,14 @@ is not being run by root. The \fIname\fR string may not begin with / or include .. as a pathname component. The format of the options file is described below. .TP +.B callback \fIserver/number +When compiled with the CBCP extensions (-DCBCP_SUPPORT) the ppp daemon +can act as a client to servers which provide CBCP-protocol callback +negotiation or act as a \fIserver. It reads its options from +/etc/ppp/callback-users and invokes /etc/ppp/callback-server +when dialing out. Otherwise it will invoke /etc/ppp/callback-client +to wait for a call. +.TP .B connect \fIscript Usually there is something which needs to be done to prepare the link before the PPP protocol can be started; for instance, with a dial-up diff --git a/pppd/pppd.h b/pppd/pppd.h index cf9840a..f47e1b9 100644 --- a/pppd/pppd.h +++ b/pppd/pppd.h @@ -71,6 +71,10 @@ #include "eui64.h" #endif +/* for cbcp_init_hook */ +#include "cbcp.h" + + /* * Limits. */ @@ -341,6 +345,10 @@ extern struct bpf_program pass_filter; /* Filter for pkts to pass */ extern struct bpf_program active_filter; /* Filter for link-active pkts */ #endif +#ifdef CBCP_SUPPORT +extern int callback_in_progress; /*Callback running*/ +#endif + #ifdef MSLANMAN extern bool ms_lanman; /* Use LanMan password instead of NT */ /* Has meaning only with MS-CHAP challenges */ @@ -500,6 +508,8 @@ void unlock_db __P((void)); /* Procedures exported from tty.c. */ void tty_init __P((void)); +void setup_serial __P((char *)); +int callback __P ((void)); /* Procedures exported from utils.c. */ void log_packet __P((u_char *, int, char *, int)); @@ -525,6 +535,7 @@ ssize_t complete_read __P((int, void *, size_t)); /* read a complete buffer */ /* Procedures exported from auth.c */ +void network_phase __P((int)); /* the dataexchanger CP-s goung up */ void link_required __P((int)); /* we are starting to use the link */ void start_link __P((int)); /* bring the link up now */ void link_terminated __P((int)); /* we are finished with the link */ @@ -712,6 +723,7 @@ extern int (*allowed_address_hook) __P((u_int32_t addr)); extern void (*ip_up_hook) __P((void)); extern void (*ip_down_hook) __P((void)); extern void (*ip_choose_hook) __P((u_int32_t *)); +extern void (*cbcp_init_hook) __P((cbcp_state *)); extern int (*chap_check_hook) __P((void)); extern int (*chap_passwd_hook) __P((char *user, char *passwd)); diff --git a/pppd/sys-linux.c b/pppd/sys-linux.c index b675c97..bda4ca6 100644 --- a/pppd/sys-linux.c +++ b/pppd/sys-linux.c @@ -936,7 +936,7 @@ void set_up_tty(int tty_fd, int local) inittermios = tios; tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL); - tios.c_cflag |= CS8 | CREAD | HUPCL; + tios.c_cflag |= CS8 | CREAD | HUPCL | CLOCAL; tios.c_iflag = IGNBRK | IGNPAR; tios.c_oflag = 0; diff --git a/pppd/tty.c b/pppd/tty.c index d571b11..295b52b 100644 --- a/pppd/tty.c +++ b/pppd/tty.c @@ -97,6 +97,10 @@ #include "pppd.h" #include "fsm.h" #include "lcp.h" +#ifdef CBCP_SUPPORT +#include "cbcp.h" +#endif /* CBCP_SUPPORT */ + void tty_process_extra_options __P((void)); void tty_check_options __P((void)); @@ -514,12 +518,10 @@ tty_check_options() int connect_tty() { char *connector; - int fdflags; -#ifndef __linux__ - struct stat statbuf; -#endif char numbuf[16]; + connector = doing_callback? callback_script: connect_script; + /* * Get a pty master/slave pair if the pty, notty, socket, * or record options were specified. @@ -547,75 +549,7 @@ int connect_tty() locked = 1; } - /* - * Open the serial device and set it up to be the ppp interface. - * First we open it in non-blocking mode so we can set the - * various termios flags appropriately. If we aren't dialling - * out and we want to use the modem lines, we reopen it later - * in order to wait for the carrier detect signal from the modem. - */ - got_sigterm = 0; - connector = doing_callback? callback_script: connect_script; - if (devnam[0] != 0) { - for (;;) { - /* If the user specified the device name, become the - user before opening it. */ - int err, prio; - - prio = privopen? OPRIO_ROOT: tty_options[0].priority; - if (prio < OPRIO_ROOT && seteuid(uid) == -1) { - error("Unable to drop privileges before opening %s: %m\n", - devnam); - status = EXIT_OPEN_FAILED; - goto errret; - } - real_ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0); - err = errno; - if (prio < OPRIO_ROOT && seteuid(0) == -1) - fatal("Unable to regain privileges"); - if (real_ttyfd >= 0) - break; - errno = err; - if (err != EINTR) { - error("Failed to open %s: %m", devnam); - status = EXIT_OPEN_FAILED; - } - if (!persist || err != EINTR) - goto errret; - } - ttyfd = real_ttyfd; - if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1 - || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) - warn("Couldn't reset non-blocking mode on device: %m"); - -#ifndef __linux__ - /* - * Linux 2.4 and above blocks normal writes to the tty - * when it is in PPP line discipline, so this isn't needed. - */ - /* - * Do the equivalent of `mesg n' to stop broadcast messages. - */ - if (fstat(ttyfd, &statbuf) < 0 - || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) { - warn("Couldn't restrict write permissions to %s: %m", devnam); - } else - tty_mode = statbuf.st_mode; -#endif /* __linux__ */ - - /* - * Set line speed, flow control, etc. - * If we have a non-null connection or initializer script, - * on most systems we set CLOCAL for now so that we can talk - * to the modem before carrier comes up. But this has the - * side effect that we might miss it if CD drops before we - * get to clear CLOCAL below. On systems where we can talk - * successfully to the modem with CLOCAL clear and CD down, - * we could clear CLOCAL at this point. - */ - set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0) - || initializer != NULL)); - } + setup_serial(connector); /* * If the pty, socket, notty and/or record option was specified, @@ -1260,3 +1194,111 @@ record_write(f, code, buf, nb, tp) } return 1; } + +void setup_serial(char *connector) +{ + int fdflags; + struct stat statbuf; + + /* + * Open the serial device and set it up to be the ppp interface. + * First we open it in non-blocking mode so we can set the + * various termios flags appropriately. If we aren't dialling + * out and we want to use the modem lines, we reopen it later + * in order to wait for the carrier detect signal from the modem. + */ + hungup = 0; + kill_link = 0; + connector = doing_callback? callback_script: connect_script; + if (devnam[0] != 0) { + for (;;) { + /* If the user specified the device name, become the + user before opening it. */ + int err, prio; + + prio = privopen? OPRIO_ROOT: tty_options[0].priority; + if (prio < OPRIO_ROOT) + seteuid(uid); + ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0); + err = errno; + if (prio < OPRIO_ROOT) + seteuid(0); + if (ttyfd >= 0) + break; + errno = err; + if (err != EINTR) { + error("Failed to open %s: %m", devnam); + status = EXIT_OPEN_FAILED; + } + if (!persist || err != EINTR) + return -1; + } + real_ttyfd = ttyfd; + if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1 + || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) + warn("Couldn't reset non-blocking mode on device: %m"); + + /* + * Do the equivalent of `mesg n' to stop broadcast messages. + */ + if (fstat(ttyfd, &statbuf) < 0 + || fchmod(ttyfd, statbuf.st_mode & ~S_IWOTH) < 0) { + warn("Couldn't restrict write permissions to %s: %m", devnam); + } else + tty_mode = statbuf.st_mode; + + /* + * Set line speed, flow control, etc. + * If we have a non-null connection or initializer script, + * on most systems we set CLOCAL for now so that we can talk + * to the modem before carrier comes up. But this has the + * side effect that we might miss it if CD drops before we + * get to clear CLOCAL below. On systems where we can talk + * successfully to the modem with CLOCAL clear and CD down, + * we could clear CLOCAL at this point. + */ + set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0) + || initializer != NULL)); + } +} + +#ifdef CBCP_SUPPORT +int callback() +{ + char *s; + char numbuf[16]; + + cbcp_protent.enabled_flag = 0; /* Already not need */ + s = cbcp_get_script(); + syslog(LOG_INFO, "Callback with <%s>",s ); + + set_up_tty( ttyfd, 1 ); + + if (real_ttyfd != -1) { + if (!default_device && modem) { + setdtr(real_ttyfd, 0); /* in case modem is off hook */ + sleep(1); + setdtr(real_ttyfd, 1); + } + } + + /* syslog(LOG_INFO, "ttyfd is %d and hungup is %d",ttyfd,hungup ); */ + if (device_script(s, ttyfd, ttyfd, 0) < 0) { + error("Callback script failed"); + status = EXIT_INIT_FAILED; + setdtr(ttyfd, 0 ); + return -1; + } + + info("Serial connection established." ); + + if (real_ttyfd != -1) + set_up_tty( real_ttyfd, 0 ); + + slprintf(numbuf, sizeof(numbuf), "%d", baud_rate); + script_setenv("SPEED", numbuf, 1); + + return ttyfd; + +} +#endif /* CBCP Support */