分类: LINUX
2017-10-16 14:05:15
void plugin_init(void) { add_options(Options); #NOTE: 增加pppoe插件的options info("RP-PPPoE plugin version %s compiled against pppd %s",RP_VERSION, VERSION); } static option_t Options[] = { { "device name", o_wild, (void *) &PPPoEDevnameHook, "PPPoE device name", OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, devnam}, { "rp_pppoe_service", o_string, &pppd_pppoe_service,"Desired PPPoE service name" }, { "rp_pppoe_ac", o_string, &acName,"Desired PPPoE access concentrator name" }, { "rp_pppoe_sess", o_string, &existingSession,"Attach to existing session (sessid:macaddr)" }, { "rp_pppoe_verbose", o_int, &printACNames,"Be verbose about discovered access concentrators"}, { "pppoe-mac", o_string, &pppoe_reqd_mac,"Only connect to specified MAC address" }, { "host-uniq", o_string, &host_uniq,"Specify custom Host-Uniq" }, { NULL } }; |
void plugin_init(void) { pap_check_hook = radius_secret_check; #NOTE: pap_check_hook钩子使用radius钩子 pap_auth_hook = radius_pap_auth; chap_check_hook = radius_secret_check; #NOTE:chap_check_hook钩子使用radius钩子 chap_verify_hook = radius_chap_verify; ip_choose_hook = radius_choose_ip; allowed_address_hook = radius_allowed_address; add_notifier(&ip_up_notifier, radius_ip_up, NULL); add_notifier(&ip_down_notifier, radius_ip_down, NULL); memset(&rstate, 0, sizeof(rstate)); strlcpy(rstate.config_file, "/etc/radiusclient/radiusclient.conf",sizeof(rstate.config_file)); #NOTE:radius客户端配置文件 add_options(Options); #NOTE: 增加radius相关的options info("RADIUS plugin initialized."); }
static option_t Options[] = {
{ "radius-config-file", o_string, &config_file }, { "avpair", o_special, add_avp }, { "map-to-ttyname", o_bool, &portnummap,"Set Radius NAS-Port attribute value via libradiusclient library", OPT_PRIO | 1 }, { "map-to-ifname", o_bool, &portnummap,"Set Radius NAS-Port attribute to number as in interface name (Default)", OPT_PRIOSUB | 0 }, { NULL } }; |
auth_order radius #NOTE: 认证顺序,可选值为 radius: 采用radius认证 # local: 采用本地认证 # radius,local: 先radius认证,再本地认证 login_tries 4 #NOTE: 最大登录尝试次数 login_timeout 60 #NOTE: 登录超时时间 nologin /etc/nologin #NOTE: nolgin文件 issue /etc/radiusclient/issue #NOTE: issue文件 authserver localhost:1812 #NOTE: 认证服务器及端口地址 acctserver localhost:1813 #NOTE: 计费服务器及端口地址 servers /etc/radiusclient/servers #NOTE: 共享秘钥存储文件 dictionary /etc/radiusclient/dictionary #NOTE: radius客户端字典 login_radius /sbin/login.radius #NOTE: radius认证登录调用的程序 seqfile /var/run/radius.seq #NOTE: radius通信服务器通信序列号缓存文件 mapfile /etc/radiusclient/port-id-map #NOTE: ttyname和NAS-Port映射文件 default_realm #NOTE: 默认认证realm radius_timeout 10 #NOTE: radius响应超时时间 radius_retries 3 #NOTE: 切换下一个服务器时尝试的次数 # nas_identifier MyUniqueNASName #NOTE: NAS唯一标识 login_local /bin/login #NOTE: 本地登录时调用的应用程序 |
radius.c ------------ static int radius_secret_check(void) { return 1; #NOTE:检测强制返回1 } File: auth.c ------------------------- static int have_pap_secret(lacks_ipp) int *lacks_ipp; { FILE *f; int ret; char *filename; struct wordlist *addrs; /* let the plugin decide, if there is one */ if (pap_check_hook) { #NOTE: 钩子pap_check_hook生效位置. ret = (*pap_check_hook)(); if (ret >= 0) return ret; } filename = _PATH_UPAPFILE; f = fopen(filename, "r"); if (f == NULL) return 0; ret = scan_authfile(f, (explicit_remote? remote_name: NULL), our_name, NULL, &addrs, NULL, filename, 0); fclose(f); if (ret >= 0 && !some_ip_ok(addrs)) { if (lacks_ipp != 0) *lacks_ipp = 1; ret = -1; } if (addrs != 0) free_wordlist(addrs); return ret >= 0; } static int have_chap_secret(client, server, need_ip, lacks_ipp) char *client; char *server; int need_ip; int *lacks_ipp; { FILE *f; int ret; char *filename; struct wordlist *addrs; if (chap_check_hook) { #NOTE: 钩子chap_check_hook生效位置. ret = (*chap_check_hook)(); if (ret >= 0) { return ret; } } filename = _PATH_CHAPFILE; f = fopen(filename, "r"); if (f == NULL) return 0; if (client != NULL && client[0] == 0) client = NULL; else if (server != NULL && server[0] == 0) server = NULL; ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0); fclose(f); if (ret >= 0 && need_ip && !some_ip_ok(addrs)) { if (lacks_ipp != 0) *lacks_ipp = 1; ret = -1; }
if (addrs != 0)
free_wordlist(addrs); return ret >= 0; } 上述流程可以看出,引入radius插件radius.so后,pap本地认证和chap本地认证过程将被跳过. 上述过程在main() 的auth_check_options()中被调用,用于确定支持的认证方式. 上述直接返回1,表明radius支持pap认证和chap认证. |
main() { ...... auth_check_options() #NOTE: 认证方式的检测 ...... for(;;){ ...... lcp_open(0); /* Start protocol */ start_link(0); while (phase != PHASE_DEAD) { handle_events(); get_input(); #NOTE: 调用get_input()方法进行数据包的处理 if (kill_link) lcp_close(0, "User request"); if (asked_to_quit) { bundle_terminating = 1; if (phase == PHASE_MASTER) mp_bundle_terminated(); } if (open_ccp_flag) { if (phase == PHASE_NETWORK || phase == PHASE_RUNNING) { ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */ (*ccp_protent.open)(0); } } } /* restore FSMs to original state */ lcp_close(0, ""); } static void get_input() {
int len, i;
u_char *p; u_short protocol; struct protent *protp; p = inpacket_buf; /* point to beginning of packet buffer */ len = read_packet(inpacket_buf); #NOTE: 读取输入数据包 if (len < 0) return; if (len == 0) { if (bundle_eof && multilink_master) { notice("Last channel has disconnected"); mp_bundle_terminated(); return; } notice("Modem hangup"); hungup = 1; if (status == EXIT_OK) status = EXIT_HANGUP; lcp_lowerdown(0); /* serial link is no longer available */ link_terminated(0); return; } dbglog("received short packet:%.*B", len, p); return; } dump_packet("rcvd", p, len); if (snoop_recv_hook) snoop_recv_hook(p, len); p += 2; /* Skip address and control */ GETSHORT(protocol, p); len -= PPP_HDRLEN; /* * Toss all non-LCP packets unless LCP is OPEN. */ if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) { dbglog("Discarded non-LCP packet when LCP not open"); return; } /* * Until we get past the authentication phase, toss all packets * except LCP, LQR and authentication packets. */ if (phase <= PHASE_AUTHENTICATE && !(protocol == PPP_LCP || protocol == PPP_LQR || protocol == PPP_PAP || protocol == PPP_CHAP || protocol == PPP_EAP)) { dbglog("discarding proto 0x%x in phase %d", protocol, phase); return; } * Upcall the proper protocol input routine. */ for (i = 0; (protp = protocols[i]) != NULL; ++i) { if (protp->protocol == protocol && protp->enabled_flag) { (*protp->input)(0, p, len); #NOTE:此处即是回调PAP协议的input()方法从而回调认证钩子函数. return; } if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag && protp->datainput != NULL) { (*protp->datainput)(0, p, len); return; } } if (debug) { const char *pname = protocol_name(protocol); if (pname != NULL) warn("Unsupported protocol '%s' (0x%x) received", pname, protocol); else warn("Unsupported protocol 0x%x received", protocol); } lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN); } File: upap.c ----------------------- struct protent pap_protent = { PPP_PAP, upap_init, upap_input, #NOTE:pap协议的input方法
......
} 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(("pap_input: rcvd short header.")); return; } GETCHAR(code, inp); GETCHAR(id, inp); GETSHORT(len, inp); if (len < UPAP_HEADERLEN) { UPAPDEBUG(("pap_input: rcvd illegal length.")); return; } if (len > l) { UPAPDEBUG(("pap_input: rcvd short packet.")); return; } len -= UPAP_HEADERLEN;
switch (code) {
case UPAP_AUTHREQ: upap_rauthreq(u, inp, id, len); #NOTE: pap请求的认证请求 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; } } static void upap_rauthreq(u, inp, id, len) upap_state *u; u_char *inp; int id; int len; {
.....
retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, rpasswdlen, &msg); #NOTE: 密码检测函数
......
} File: auth.c --------------------------- int check_passwd(unit, auser, userlen, apasswd, passwdlen, msg) int unit; char *auser; int userlen; char *apasswd; int passwdlen; char **msg; { int ret; char *filename; FILE *f; struct wordlist *addrs = NULL, *opts = NULL; char passwd[256], user[256]; char secret[MAXWORDLEN]; static int attempts = 0 slprintf(passwd, sizeof(passwd), "%.*v", passwdlen, apasswd); slprintf(user, sizeof(user), "%.*v", userlen, auser); *msg = ""; /* * Check if a plugin wants to handle this. */ if (pap_auth_hook) { #NOTE: pap_auth_hook()钩子生效点. ret = (*pap_auth_hook)(user, passwd, msg, &addrs, &opts); #NOTE: 调用对应的插件钩子认证函数 if (ret >= 0) { /* note: set_allowed_addrs() saves opts (but not addrs): don't free it! */ if (ret) set_allowed_addrs(unit, addrs, opts); else if (opts != 0) free_wordlist(opts); if (addrs != 0) free_wordlist(addrs); BZERO(passwd, sizeof(passwd)); return ret? UPAP_AUTHACK: UPAP_AUTHNAK; #NOTE: 处理完后即返回,未比对本地配置文件中的内容 } } addrs = opts = NULL; ret = UPAP_AUTHNAK; f = fopen(filename, "r"); if (f == NULL) { error("Can't open PAP password file %s: %m", filename); } else { check_access(f, filename); if (scan_authfile(f, user, our_name, secret, &addrs, &opts, filename, 0) < 0) { warn("no PAP secret found for %s", user); } else { /* * If the secret is "@login", it means to check * the password against the login database. */ int login_secret = strcmp(secret, "@login") == 0; ret = UPAP_AUTHACK; if (uselogin || login_secret) { /* login option or secret is @login */ if (session_full(user, passwd, devnam, msg) == 0) { ret = UPAP_AUTHNAK; } } else if (session_mgmt) { if (session_check(user, NULL, devnam, NULL) == 0) { warn("Peer %q failed PAP Session verification", user); ret = UPAP_AUTHNAK; } } /* password given in pap-secrets - must match */ if (cryptpap || strcmp(passwd, secret) != 0) { char *cbuf = crypt(passwd, secret); if (!cbuf || strcmp(cbuf, secret) != 0) ret = UPAP_AUTHNAK; } } } fclose(f); } if (ret == UPAP_AUTHNAK) { if (**msg == 0) *msg = "Login incorrect"; /* * XXX can we ever get here more than once?? * Frustrate passwd stealer programs. * Allow 10 tries, but start backing off after 3 (stolen from login). * On 10'th, drop the connection. */ if (attempts++ >= 10) { warn("%d LOGIN FAILURES ON %s, %s", attempts, devnam, user); lcp_close(unit, "login failed"); } if (attempts > 3) sleep((u_int) (attempts - 3) * 5); if (opts != NULL) free_wordlist(opts); } else { attempts = 0; /* Reset count */ if (**msg == 0) *msg = "Login ok"; set_allowed_addrs(unit, addrs, opts); } if (addrs != NULL) free_wordlist(addrs); BZERO(passwd, sizeof(passwd)); BZERO(secret, sizeof(secret)); return ret; } chap_verify_hook()钩子函数的调用方式也是一致的,大体流程如下: File: chap-new.c ------------------------------- struct protent chap_protent = { PPP_CHAP, chap_init, chap_input, #NOTE: chap协议的input()方法 ...... } static void chap_input(int unit, unsigned char *pkt, int pktlen) { struct chap_client_state *cs = &client; struct chap_server_state *ss = &server; unsigned char code, id; int len; if (pktlen < CHAP_HDRLEN) return; GETCHAR(code, pkt); GETCHAR(id, pkt); GETSHORT(len, pkt); if (len < CHAP_HDRLEN || len > pktlen) return; len -= CHAP_HDRLEN; switch (code) { case CHAP_CHALLENGE: chap_respond(cs, id, pkt, len); break; case CHAP_RESPONSE: chap_handle_response(ss, id, pkt, len); #NOTE: 处理挑战回复 break; case CHAP_FAILURE: case CHAP_SUCCESS: chap_handle_status(cs, code, id, pkt, len); break; } } unsigned char *pkt, int len) { if (ss->flags & CHALLENGE_VALID) { ......
if (chap_verify_hook) #NOTE: chap_verify_hook()钩子函数生效点
verifier = chap_verify_hook; else verifier = chap_verify_response; ok = (*verifier)(name, ss->name, id, ss->digest, ss->challenge + PPP_HDRLEN + CHAP_HDRLEN, response, ss->message, sizeof(ss->message)); if (!ok || !auth_number()) { ss->flags |= AUTH_FAILED; warn("Peer %q failed CHAP authentication", name); } } else if ((ss->flags & AUTH_DONE) == 0) return; ........
}
|