Chinaunix首页 | 论坛 | 博客
  • 博客访问: 234191
  • 博文数量: 54
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 431
  • 用 户 组: 普通用户
  • 注册时间: 2014-07-26 09:36
文章分类

全部博文(54)

分类: LINUX

2017-10-16 14:05:15

1. 软件版本:
    ppp-2.4.7.tar.gz

2. 源码框架:

3. 源码详解:  
      对pppd而言,pppoe,radius本质上是它的两个插件(pppd插件参见博文"PPPD源码分析").
     先来看看pppoe的插件rp-pppoe.so源码:
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 }
};

      radius插件radius.so的源码如下:
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 }
};

     
     radius的NAS配置文件为radiusclient.conf,其内容如下:
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: 本地登录时调用的应用程序



      上述可以看出pppoe和radius是两个独立的插件模块,要想协同工作,需要将其关联起来.
      radius与pppoe的关联主要涉及如下两个option:
        plugin                     /lib/pppd/2.4.7radius.so                    #NOTE:使能radius插件
        radius-config-file    /etc/radiusclient/radiusclient.conf     #NOTE:指定radius配置文件
      上述两个Option加入到pppoe的options文件中,则可以将pppoe和radius关联起来.


      radius插件主要用于认证,授权,和计费.结合pppd而言,先来看看,radius插件的加入,是如何影响原有的认证的过程的.
先看看上述两个check_hook是如何工作的:
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认证.

     
     下面再看看pap_auth_hook()和chap_verify_hook()钩子函数的的作用位置:
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;
    }

    if (len < PPP_HDRLEN) {
        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: 处理完后即返回,未比对本地配置文件中的内容
        }
    }

    filename = _PATH_UPAPFILE;
    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;
                }
            }

            if (secret[0] != 0 && !login_secret) {
                /* 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;
        }
}

static void chap_handle_response(struct chap_server_state *ss, int id,
                     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;
     
        ........
}

阅读(2906) | 评论(0) | 转发(0) |
0

上一篇:xl2tpd源码分析

下一篇:没有了

给主人留下些什么吧!~~