How to enable Freeradius to support a smart card with AES encryption algorithm?
Hi! I have a smart card emluator which suports AES, not MD5 encryption algorithm. Is it possible to enable Freeradius to support my smart card emlulator? I have an idea as follow: First,amending client agent (NAS) daemon program to make it send chap-password which is produced with AES, not MD5. The usual md5 chap-password is produced as MD5( user-packet-ID+user-secret+16 bytes authenticator), while the aes chap-password is produced as AES(16 bytes authenticator) using user-secret as key.The usual md5 chap-passwor attribute in an Access Request packet is as follow: __________________________________________________ | code = 3 | Length = 19 | user-packet-ID | 16 bytes value| __________________________________________________ While the aes chap-password replaced the 16 bytes value ( MD5( user-packet-ID+user-secret+16 bytes authenticator)) with AES(16 bytes authenticator). Second ,amending rlm-chap.c to alter it to use AES to analyze the request packet. Is it practical? Appreciate any suggestions. regards Guoxian
yao guoxian wrote:
Hi! I have a smart card emluator which suports AES, not MD5 encryption algorithm. Is it possible to enable Freeradius to support my smart card emlulator?
Edit the code.
I have an idea as follow: First,amending client agent (NAS) daemon program to make it send chap-password which is produced with AES, not MD5.
Don't do that. It isn't CHAP, and you will break a lot of things.
The usual md5 chap-password is produced as MD5( user-packet-ID+user-secret+16 bytes authenticator), while the aes chap-password is produced as AES(16 bytes authenticator) using user-secret as key.The usual md5 chap-passwor attribute in an Access Request packet is as follow: __________________________________________________
| code = 3 | Length = 19 | user-packet-ID | 16 bytes value| __________________________________________________ While the aes chap-password replaced the 16 bytes value ( MD5( user-packet-ID+user-secret+16 bytes authenticator)) with AES(16 bytes authenticator). Second ,amending rlm-chap.c to alter it to use AES to analyze the request packet. Is it practical? Appreciate any suggestions.
No, it's not practical. What you're missing is that none of the NASes will do the AES calculation, so changing FreeRADIUS won't help. If you control the software on the NAS, just invent a new attribute, "My-AES-Password", and use that. That's what attributes are for. Then, write a new module to support that attribute. That's what modules are for. Hacking existing attributes and modules is a recipe for disaster. Don't do it. Alan DeKok. -- http://deployingradius.com - The web site of the book http://deployingradius.com/blog/ - The blog
Thanks, Alan. But I have lots of problems. First, what is difference between challenge-response and chap?In my opinion, challenge-response is a authentication mechanism and flow while chap is a method to hide and transport user' password. In challenge-response, the random challenge is produced by the radius server and has not length limited while the 16 bytes random authenticator used in chap is produced by the NAS or the client. Is that right? Second,suppose we have enabled the NAS(client) and Freeradius to support our specified attribute "My-Aes-Password" , how to write the new module to handle the attribute? Is it like follows: 1. code a program like freeradius-parth/src/modules/rlm_example/rlm_exmple.c and name it as rlm_aes; 2. compile it and store the aes.exe file in the /bin/ directory; 3. edit the radiusd.conf as follow: #in the modules section ... exec aes{ wait = yes; program ="/bin/aes %My_Aes_Password " input_pairs = request output_pairs = reply } ... authorize { preprocess chap mschap suffix sql aes } ... or we needn't compile the rlm_aes.c and just leave it to the Freeradius to do what need to do ? Third , how to enable Freeradius and Nas(client) to support our new attribute?Does it need to append the dictionary file a new entry? regards Guoxian 2007/2/1, Alan DeKok <aland@deployingradius.com>:
yao guoxian wrote:
Hi! I have a smart card emluator which suports AES, not MD5 encryption algorithm. Is it possible to enable Freeradius to support my smart card emlulator?
Edit the code.
I have an idea as follow: First,amending client agent (NAS) daemon program to make it send chap-password which is produced with AES, not MD5.
Don't do that. It isn't CHAP, and you will break a lot of things.
The usual md5 chap-password is produced as MD5( user-packet-ID+user-secret+16 bytes authenticator), while the aes chap-password is produced as AES(16 bytes authenticator) using user-secret as key.The usual md5 chap-passwor attribute in an Access Request packet is as follow: __________________________________________________
| code = 3 | Length = 19 | user-packet-ID | 16 bytes value| __________________________________________________ While the aes chap-password replaced the 16 bytes value ( MD5( user-packet-ID+user-secret+16 bytes authenticator)) with AES(16 bytes authenticator). Second ,amending rlm-chap.c to alter it to use AES to analyze the request packet. Is it practical? Appreciate any suggestions.
No, it's not practical.
What you're missing is that none of the NASes will do the AES calculation, so changing FreeRADIUS won't help.
If you control the software on the NAS, just invent a new attribute, "My-AES-Password", and use that. That's what attributes are for. Then, write a new module to support that attribute. That's what modules are for.
Hacking existing attributes and modules is a recipe for disaster. Don't do it.
Alan DeKok. -- http://deployingradius.com - The web site of the book http://deployingradius.com/blog/ - The blog - List info/subscribe/unsubscribe? See http://www.freeradius.org/list/users.html
yao guoxian wrote:
Second,suppose we have enabled the NAS(client) and Freeradius to support our specified attribute "My-Aes-Password" , how to write the new module to handle the attribute?
See the examples & the documentation. What about them is unclear?
Third , how to enable Freeradius and Nas(client) to support our new attribute?Does it need to append the dictionary file a new entry?
All of this is documented. Alan DeKok. -- http://deployingradius.com - The web site of the book http://deployingradius.com/blog/ - The blog
Thanks,Alan. But I have a few questions. First, if I create a new attribute "My-Aes-Password" and include it in the Access-Requet packet, I should not include the attributes such as "User-Password" or "Chap-Password".Is it right? For I have read RFC 2865, and gotten the message from page 64th as "[Note 1] An Access-Request MUST contain either a User-Password or a CHAP-Password or State. An Access-Request MUST NOT contain both a User-Password and a CHAP-Password. If future extensions allow other kinds of authentication information to be conveyed, the attribute for that can be used in an Access-Request instead of User-Password or CHAP-Password.", I have this question. The second question is about how to write modules.Sorry to ask the same question,but I want to verify my plan to see if it is pratical. The plan is as follow: I dont amend the module "rlm_chap" , I just copy all files in the ./src/modules/rlm_chap/ to a new dictory "rlm_aes" and rename files rlm_chap.* to rlm_aes.*. Then I edit rlm-chap.c to alter it to use AES to analyze the request packet. Is it pratical? 2007/2/3, Alan DeKok <aland@deployingradius.com>:
yao guoxian wrote:
Second,suppose we have enabled the NAS(client) and Freeradius to support our specified attribute "My-Aes-Password" , how to write the new module to handle the attribute?
See the examples & the documentation. What about them is unclear?
Third , how to enable Freeradius and Nas(client) to support our new attribute?Does it need to append the dictionary file a new entry?
All of this is documented.
Alan DeKok. -- http://deployingradius.com - The web site of the book http://deployingradius.com/blog/ - The blog - List info/subscribe/unsubscribe? See http://www.freeradius.org/list/users.html
yao guoxian wrote:
Thanks,Alan. But I have a few questions. First, if I create a new attribute "My-Aes-Password" and include it in the Access-Requet packet, I should not include the attributes such as "User-Password" or "Chap-Password".Is it right?
Yes.
The second question is about how to write modules.
There is documentation. See "rlm_example", for one.
Sorry to ask the same question,but I want to verify my plan to see if it is pratical. The plan is as follow: I dont amend the module "rlm_chap" , I just copy all files in the ./src/modules/rlm_chap/ to a new dictory "rlm_aes" and rename files rlm_chap.* to rlm_aes.*. Then I edit rlm-chap.c to alter it to use AES to analyze the request packet. Is it pratical?
Yes. Alan DeKok. -- http://deployingradius.com - The web site of the book http://deployingradius.com/blog/ - The blog
Thanks,Alan. To create a new attribute, I did the following things. (1).I edited the file "src/include/radius.h" and add a line like "#define PW_AES_PASSWORD 192" . (2).I also edited the file "/usr/local/share/freeradius/dictionary" and insert a line like "ATTRIBUTE AES-PASSWORD 192 octets " . (3).Finally, I recompiled Freeradius . Is it right and enough to create a new attribute "AES-PASSWORD"? To create a new module,I did the following things. (1).Icopy src/rlm_example/rlm_example.c to another dictory. (2).amend the rlm_example to make it support AES encryption algorithm, main changes are as follow: static int example_authorize(void *instance, REQUEST *request) { /* quiet the compiler */ instance = instance; request = request; /* * Look for the 'state' attribute. */ // state = pairfind(request->packet->vps, PW_STATE); // if (state != NULL) { // DEBUG("rlm_example: Found reply to access challenge"); // return RLM_MODULE_OK; // } /* * Create the challenge, and add it to the reply. */ // reply = pairmake("Reply-Message", "This is a challenge", T_OP_EQ); // pairadd(&request->reply->vps, reply); // state = pairmake("State", "0", T_OP_EQ); // pairadd(&request->reply->vps, state); /* * Mark the packet as an Access-Challenge packet. * * The server will take care of sending it to the user. */ // request->reply->code = PW_ACCESS_CHALLENGE; // DEBUG("rlm_example: Sending Access-Challenge."); // return RLM_MODULE_HANDLED; return RLM_MODULE_OK; } note: I have commented main part of lines of funtion example_authorize. static int example_authenticate(void *instance, REQUEST *request) { VALUE_PAIR *passwd_item; char pass_str[MAX_STRING_LEN]; VALUE_PAIR *module_fmsg_vp; char module_fmsg[MAX_STRING_LEN]; /* quiet the compiler */ instance = instance; request = request; if (!request->username) { radlog(L_AUTH, "rlm_aes: Attribute \"User-Name\" is required for authentication.\n"); return RLM_MODULE_INVALID; } if (!request->password) { radlog(L_AUTH, "rlm_aes: Attribute \"AES-Password\" is required for authentication."); return RLM_MODULE_INVALID; } if (request->password->attribute != PW_AES_PASSWORD) { radlog(L_AUTH, "rlm_aes: Attribute \"AES-Password\" is required for authentication. Cannot use \"%s\".", request->password->name); return RLM_MODULE_INVALID; } if (request->password->length == 0) { radlog(L_ERR, "rlm_aes: empty password supplied"); return RLM_MODULE_INVALID; } /* * * Don't print out the CHAP password here. It's binary crap. * */ DEBUG(" rlm_aes: login attempt by \"%s\" with AES password", request->username->strvalue); if ((passwd_item = pairfind(request->config_items, PW_PASSWORD)) == NULL){ DEBUG(" rlm_aes: Could not find clear text password for user %s",request->username->strvalue); snprintf(module_fmsg,sizeof(module_fmsg),"rlm_aes: Clear text password not available"); module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ); pairadd(&request->packet->vps, module_fmsg_vp); return RLM_MODULE_INVALID; } DEBUG(" rlm_aes: Using clear text password %s for user %s authentication.", passwd_item->strvalue, request->username->strvalue); rad_aes_encode(request->packet,pass_str,request->password->strvalue[0],passwd_item); if (memcmp(pass_str+1,request->password->strvalue+1,CHAP_VALUE_LENGTH) != 0){ DEBUG(" rlm_aes: Pasword check failed"); snprintf(module_fmsg,sizeof(module_fmsg),"rlm_aes: Wrong user password"); module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ); pairadd(&request->packet->vps, module_fmsg_vp); return RLM_MODULE_REJECT; } DEBUG(" rlm_aes: chap user %s authenticated succesfully",request->username->strvalue); return RLM_MODULE_OK; } note: I have use rad_aes_encode(request->packet,pass_str,request->password->strvalue[0],passwd_item); to enable AES password. (3). edit the src/lib/radiusc.c and insert my funtion rad_aes_encode. (4). recompile Freeradius. The following is part of result to the command " radiusd -X": Module: Loaded example example: integer = 1 example: boolean = no example: string = "(null)" Module: Instantiated example (example) But when I send packets like follow to Freeradius server: +-----------------------------------------------------------------------------------+ | UserName="test" | ID = n | length = m | +-------------------------------------------------------------------------------------------------------------------------------+ | 192(Identify "AES-PASSWORD")| length =19 | ID = n | 16 Bytes AES encrypted text | +-------------------------------------------------------------------------------------------------------------------------------- | .......... | +----------------------------------------------------------------------- I got the following message: WARNING: Malformed RADIUS packet from host 202.117.7.223: packet attributes do NOT exactly fill the packet --- Walking the entire request list --- Nothing to do. Sleeping until we see a request. Thanks for any suggestion! 2007/3/14, Alan DeKok <aland@deployingradius.com>:
yao guoxian wrote:
Thanks,Alan. But I have a few questions. First, if I create a new attribute "My-Aes-Password" and include it in the Access-Requet packet, I should not include the attributes such as "User-Password" or "Chap-Password".Is it right?
Yes.
The second question is about how to write modules.
There is documentation. See "rlm_example", for one.
Sorry to ask the same question,but I want to verify my plan to see if it is pratical. The plan is as follow: I dont amend the module "rlm_chap" , I just copy all files in the ./src/modules/rlm_chap/ to a new dictory "rlm_aes" and rename files rlm_chap.* to rlm_aes.*. Then I edit rlm-chap.c to alter it to use AES to analyze the request packet. Is it pratical?
Yes.
Alan DeKok. -- http://deployingradius.com - The web site of the book http://deployingradius.com/blog/ - The blog - List info/subscribe/unsubscribe? See http://www.freeradius.org/list/users.html
Sorry , Alan. The most recently response to the "Access-request "packet from "radiusd -X" monitor are as follows( only a segment related to the request): ...... auth: type Local auth: No User-Password or CHAP-Password attribute in the request auth: Failed to validate the user. Delaying request 8 for 1 seconds Finished request 8 Going to the next request --- Walking the entire request list --- Waking up in 1 seconds... --- Walking the entire request list --- Waking up in 1 seconds... --- Walking the entire request list --- Sending Access-Reject of id 1 to 202.117.7.223:1738 Waking up in 4 seconds... --- Walking the entire request list --- Cleaning up request 8 ID 1 with timestamp 45fa6552 Nothing to do. Sleeping until we see a request. The complete display of "radiusd -X" monitor: Starting - reading configuration files ... reread_config: reading radiusd.conf Config: including file: /usr/local/etc/raddb/proxy.conf Config: including file: /usr/local/etc/raddb/clients.conf Config: including file: /usr/local/etc/raddb/snmp.conf Config: including file: /usr/local/etc/raddb/eap.conf Config: including file: /usr/local/etc/raddb/sql.conf main: prefix = "/usr/local" main: localstatedir = "/usr/local/var" main: logdir = "/usr/local/var/log/radius" main: libdir = "/usr/local/lib" main: radacctdir = "/usr/local/var/log/radius/radacct" main: hostname_lookups = no main: max_request_time = 30 main: cleanup_delay = 5 main: max_requests = 1024 main: delete_blocked_requests = 0 main: port = 0 main: allow_core_dumps = no main: log_stripped_names = no main: log_file = "/usr/local/var/log/radius/radius.log" main: log_auth = no main: log_auth_badpass = no main: log_auth_goodpass = no main: pidfile = "/usr/local/var/run/radiusd/radiusd.pid" main: user = "(null)" main: group = "(null)" main: usercollide = no main: lower_user = "no" main: lower_pass = "no" main: nospace_user = "no" main: nospace_pass = "no" main: checkrad = "/usr/local/sbin/checkrad" main: proxy_requests = yes proxy: retry_delay = 5 proxy: retry_count = 3 proxy: synchronous = no proxy: default_fallback = yes proxy: dead_time = 120 proxy: post_proxy_authorize = yes proxy: wake_all_if_all_dead = no security: max_attributes = 200 security: reject_delay = 1 security: status_server = no main: debug_level = 0 read_config_files: reading dictionary read_config_files: reading naslist Using deprecated naslist file. Support for this will go away soon. read_config_files: reading clients read_config_files: reading realms radiusd: entering modules setup Module: Library search path is /usr/local/lib Module: Loaded exec exec: wait = yes exec: program = "(null)" exec: input_pairs = "request" exec: output_pairs = "(null)" exec: packet_type = "(null)" rlm_exec: Wait=yes but no output defined. Did you mean output=none? Module: Instantiated exec (exec) Module: Loaded expr Module: Instantiated expr (expr) Module: Loaded PAP pap: encryption_scheme = "crypt" Module: Instantiated pap (pap) Module: Loaded CHAP Module: Instantiated chap (chap) Module: Loaded MS-CHAP mschap: use_mppe = yes mschap: require_encryption = no mschap: require_strong = no mschap: with_ntdomain_hack = no mschap: passwd = "(null)" mschap: authtype = "MS-CHAP" mschap: ntlm_auth = "(null)" Module: Instantiated mschap (mschap) Module: Loaded preprocess preprocess: huntgroups = "/usr/local/etc/raddb/huntgroups" preprocess: hints = "/usr/local/etc/raddb/hints" preprocess: with_ascend_hack = no preprocess: ascend_channels_per_line = 23 preprocess: with_ntdomain_hack = no preprocess: with_specialix_jetstream_hack = no preprocess: with_cisco_vsa_hack = no Module: Instantiated preprocess (preprocess) Module: Loaded realm realm: format = "suffix" realm: delimiter = "@" realm: ignore_default = no realm: ignore_null = no Module: Instantiated realm (suffix) Module: Loaded eap eap: default_eap_type = "md5" eap: timer_expire = 60 eap: ignore_unknown_eap_types = no eap: cisco_accounting_username_bug = no rlm_eap: Loaded and initialized type md5 rlm_eap: Loaded and initialized type leap gtc: challenge = "Password: " gtc: auth_type = "PAP" rlm_eap: Loaded and initialized type gtc mschapv2: with_ntdomain_hack = no rlm_eap: Loaded and initialized type mschapv2 Module: Instantiated eap (eap) Module: Loaded SQL sql: driver = "rlm_sql_mysql" sql: server = "202.117.7.243" sql: port = "" sql: login = "root" sql: password = "123" sql: radius_db = "radius" sql: acct_table = "radacct" sql: acct_table2 = "radacct" sql: authcheck_table = "radcheck" sql: authreply_table = "radreply" sql: groupcheck_table = "radgroupcheck" sql: groupreply_table = "radgroupreply" sql: usergroup_table = "usergroup" sql: nas_table = "nas" sql: dict_table = "dictionary" sql: sqltrace = yes sql: sqltracefile = "/usr/local/var/log/radius/sqltrace.sql" sql: readclients = no sql: deletestalesessions = yes sql: num_sql_socks = 5 sql: sql_user_name = "%{User-Name}" sql: default_user_profile = "" sql: query_on_not_found = no sql: authorize_check_query = "SELECT id, UserName, Attribute, Value, op FROM radcheck WHERE Username = '%{SQL-User-Name}' ORDER BY id" sql: authorize_reply_query = "SELECT id, UserName, Attribute, Value, op FROM radreply WHERE Username = '%{SQL-User-Name}' ORDER BY id" sql: authorize_group_check_query = "SELECT radgroupcheck.id, radgroupcheck.GroupName,radgroupcheck.Attribute,radgroupcheck.Value, radgroupcheck.op FROM radgroupcheck,usergroup WHERE usergroup.Username = '%{SQL-User-Name}' AND usergroup.GroupName = radgroupcheck.GroupName ORDER BY radgroupcheck.id" sql: authorize_group_reply_query = "SELECT radgroupreply.id, radgroupreply.GroupName,radgroupreply.Attribute,radgroupreply.Value, radgroupreply.op FROM radgroupreply,usergroup WHERE usergroup.Username = '%{SQL-User-Name}' AND usergroup.GroupName = radgroupreply.GroupName ORDER BY radgroupreply.id" sql: accounting_onoff_query = "UPDATE radacct SET AcctStopTime='%S', AcctSessionTime=unix_timestamp('%S') - unix_timestamp(AcctStartTime), AcctTerminateCause='%{Acct-Terminate-Cause}', AcctStopDelay = '%{Acct-Delay-Time}' WHERE AcctSessionTime=0 AND AcctStopTime=0 AND NASIPAddress= '%{NAS-IP-Address}' AND AcctStartTime <= '%S'" sql: accounting_update_query = "UPDATE radacct SET FramedIPAddress = '%{Framed-IP-Address}', AcctSessionTime = '%{Acct-Session-Time}', AcctInputOctets = '%{Acct-Input-Octets}', AcctOutputOctets = '%{Acct-Output-Octets}' WHERE AcctSessionId = '%{Acct-Session-Id}' AND UserName = '%{SQL-User-Name}' AND NASIPAddress= '%{NAS-IP-Address}'" sql: accounting_update_query_alt = "INSERT into radacct (AcctSessionId, AcctUniqueId, UserName, Realm, NASIPAddress, NASPortId, NASPortType, AcctStartTime, AcctSessionTime, AcctAuthentic, ConnectInfo_start, AcctInputOctets, AcctOutputOctets, CalledStationId, CallingStationId, ServiceType, FramedProtocol, FramedIPAddress, AcctStartDelay) values('%{Acct-Session-Id}', '%{Acct-Unique-Session-Id}', '%{SQL-User-Name}', '%{Realm}', '%{NAS-IP-Address}', '%{NAS-Port}', '%{NAS-Port-Type}', DATE_SUB('%S',INTERVAL (%{Acct-Session-Time:-0} + %{Acct-Delay-Time:-0}) SECOND), '%{Acct-Session-Time}', '%{Acct-Authentic}', '', '%{Acct-Input-Octets}', '%{Acct-Output-Octets}', '%{Called-Station-Id}', '%{Calling-Station-Id}', '%{Service-Type}', '%{Framed-Protocol}', '%{Framed-IP-Address}', '0')" sql: accounting_start_query = "INSERT into radacct (AcctSessionId, AcctUniqueId, UserName, Realm, NASIPAddress, NASPortId, NASPortType, AcctStartTime, AcctStopTime, AcctSessionTime, AcctAuthentic, ConnectInfo_start, ConnectInfo_stop, AcctInputOctets, AcctOutputOctets, CalledStationId, CallingStationId, AcctTerminateCause, ServiceType, FramedProtocol, FramedIPAddress, AcctStartDelay, AcctStopDelay) values('%{Acct-Session-Id}', '%{Acct-Unique-Session-Id}', '%{SQL-User-Name}', '%{Realm}', '%{NAS-IP-Address}', '%{NAS-Port}', '%{NAS-Port-Type}', '%S', '0', '0', '%{Acct-Authentic}', '%{Connect-Info}', '', '0', '0', '%{Called-Station-Id}', '%{Calling-Station-Id}', '', '%{Service-Type}', '%{Framed-Protocol}', '%{Framed-IP-Address}', '%{Acct-Delay-Time}', '0')" sql: accounting_start_query_alt = "UPDATE radacct SET AcctStartTime = '%S', AcctStartDelay = '%{Acct-Delay-Time}', ConnectInfo_start = '%{Connect-Info}' WHERE AcctSessionId = '%{Acct-Session-Id}' AND UserName = '%{SQL-User-Name}' AND NASIPAddress = '%{NAS-IP-Address}'" sql: accounting_stop_query = "UPDATE radacct SET AcctStopTime = '%S', AcctSessionTime = '%{Acct-Session-Time}', AcctInputOctets = '%{Acct-Input-Octets}', AcctOutputOctets = '%{Acct-Output-Octets}', AcctTerminateCause = '%{Acct-Terminate-Cause}', AcctStopDelay = '%{Acct-Delay-Time}', ConnectInfo_stop = '%{Connect-Info}' WHERE AcctSessionId = '%{Acct-Session-Id}' AND UserName = '%{SQL-User-Name}' AND NASIPAddress = '%{NAS-IP-Address}'" sql: accounting_stop_query_alt = "INSERT into radacct (AcctSessionId, AcctUniqueId, UserName, Realm, NASIPAddress, NASPortId, NASPortType, AcctStartTime, AcctStopTime, AcctSessionTime, AcctAuthentic, ConnectInfo_start, ConnectInfo_stop, AcctInputOctets, AcctOutputOctets, CalledStationId, CallingStationId, AcctTerminateCause, ServiceType, FramedProtocol, FramedIPAddress, AcctStartDelay, AcctStopDelay) values('%{Acct-Session-Id}', '%{Acct-Unique-Session-Id}', '%{SQL-User-Name}', '%{Realm}', '%{NAS-IP-Address}', '%{NAS-Port}', '%{NAS-Port-Type}', DATE_SUB('%S', INTERVAL (%{Acct-Session-Time:-0} + %{Acct-Delay-Time:-0}) SECOND), '%S', '%{Acct-Session-Time}', '%{Acct-Authentic}', '', '%{Connect-Info}', '%{Acct-Input-Octets}', '%{Acct-Output-Octets}', '%{Called-Station-Id}', '%{Calling-Station-Id}', '%{Acct-Terminate-Cause}', '%{Service-Type}', '%{Framed-Protocol}', '%{Framed-IP-Address}', '0', '%{Acct-Delay-Time}')" sql: group_membership_query = "SELECT GroupName FROM usergroup WHERE UserName='%{SQL-User-Name}'" sql: connect_failure_retry_delay = 60 sql: simul_count_query = "" sql: simul_verify_query = "SELECT RadAcctId, AcctSessionId, UserName, NASIPAddress, NASPortId, FramedIPAddress, CallingStationId, FramedProtocol FROM radacct WHERE UserName='%{SQL-User-Name}' AND AcctStopTime = 0" sql: postauth_table = "radpostauth" sql: postauth_query = "INSERT into radpostauth (id, user, pass, reply, date) values ('', '%{User-Name}', '%{User-Password:-Chap-Password}', '%{reply:Packet-Type}', NOW())" sql: safe-characters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" rlm_sql (sql): Driver rlm_sql_mysql (module rlm_sql_mysql) loaded and linked rlm_sql (sql): Attempting to connect to root@202.117.7.243:/radius rlm_sql (sql): starting 0 rlm_sql (sql): Attempting to connect rlm_sql_mysql #0 rlm_sql_mysql: Starting connect to MySQL server for #0 rlm_sql (sql): Connected new DB handle, #0 rlm_sql (sql): starting 1 rlm_sql (sql): Attempting to connect rlm_sql_mysql #1 rlm_sql_mysql: Starting connect to MySQL server for #1 rlm_sql (sql): Connected new DB handle, #1 rlm_sql (sql): starting 2 rlm_sql (sql): Attempting to connect rlm_sql_mysql #2 rlm_sql_mysql: Starting connect to MySQL server for #2 rlm_sql (sql): Connected new DB handle, #2 rlm_sql (sql): starting 3 rlm_sql (sql): Attempting to connect rlm_sql_mysql #3 rlm_sql_mysql: Starting connect to MySQL server for #3 rlm_sql (sql): Connected new DB handle, #3 rlm_sql (sql): starting 4 rlm_sql (sql): Attempting to connect rlm_sql_mysql #4 rlm_sql_mysql: Starting connect to MySQL server for #4 rlm_sql (sql): Connected new DB handle, #4 Module: Instantiated sql (sql) Module: Loaded example example: integer = 1 example: boolean = no example: string = "(null)" Module: Instantiated example (example) Module: Loaded Acct-Unique-Session-Id acct_unique: key = "User-Name, Acct-Session-Id, NAS-IP-Address, Client-IP-Address, NAS-Port" Module: Instantiated acct_unique (acct_unique) Module: Loaded detail detail: detailfile = "/usr/local/var/log/radius/radacct/%{Client-IP-Address}/detail-%Y%m%d" detail: detailperm = 384 detail: dirperm = 493 detail: locking = no Module: Instantiated detail (detail) Module: Loaded System unix: cache = no unix: passwd = "(null)" unix: shadow = "(null)" unix: group = "(null)" unix: radwtmp = "/usr/local/var/log/radius/radwtmp" unix: usegroup = no unix: cache_reload = 600 Module: Instantiated unix (unix) Module: Loaded radutmp radutmp: filename = "/usr/local/var/log/radius/radutmp" radutmp: username = "%{User-Name}" radutmp: case_sensitive = yes radutmp: check_with_nas = yes radutmp: perm = 384 radutmp: callerid = yes Module: Instantiated radutmp (radutmp) Listening on authentication *:1812 Listening on accounting *:1813 Ready to process requests. exec: input_pairs = "request" exec: output_pairs = "(null)" exec: packet_type = "(null)" rlm_exec: Wait=yes but no output defined. Did you mean output=none? Module: Instantiated exec (exec) Module: Loaded expr Module: Instantiated expr (expr) Module: Loaded PAP pap: encryption_scheme = "crypt" Module: Instantiated pap (pap) Module: Loaded CHAP Module: Instantiated chap (chap) Module: Loaded MS-CHAP mschap: use_mppe = yes mschap: require_encryption = no mschap: require_strong = no mschap: with_ntdomain_hack = no mschap: passwd = "(null)" mschap: authtype = "MS-CHAP" mschap: ntlm_auth = "(null)" Module: Instantiated mschap (mschap) Module: Loaded preprocess preprocess: huntgroups = "/usr/local/etc/raddb/huntgroups" preprocess: hints = "/usr/local/etc/raddb/hints" preprocess: with_ascend_hack = no preprocess: ascend_channels_per_line = 23 preprocess: with_ntdomain_hack = no preprocess: with_specialix_jetstream_hack = no preprocess: with_cisco_vsa_hack = no Module: Instantiated preprocess (preprocess) Module: Loaded realm realm: format = "suffix" realm: delimiter = "@" realm: ignore_default = no realm: ignore_null = no Module: Instantiated realm (suffix) Module: Loaded eap eap: default_eap_type = "md5" eap: timer_expire = 60 eap: ignore_unknown_eap_types = no eap: cisco_accounting_username_bug = no rlm_eap: Loaded and initialized type md5 rlm_eap: Loaded and initialized type leap gtc: challenge = "Password: " gtc: auth_type = "PAP" rlm_eap: Loaded and initialized type gtc mschapv2: with_ntdomain_hack = no rlm_eap: Loaded and initialized type mschapv2 Module: Instantiated eap (eap) Module: Loaded SQL sql: driver = "rlm_sql_mysql" sql: server = "202.117.7.243" sql: port = "" sql: login = "root" sql: password = "123" sql: radius_db = "radius" sql: acct_table = "radacct" sql: acct_table2 = "radacct" sql: authcheck_table = "radcheck" sql: authreply_table = "radreply" sql: groupcheck_table = "radgroupcheck" sql: groupreply_table = "radgroupreply" sql: usergroup_table = "usergroup" sql: nas_table = "nas" sql: dict_table = "dictionary" sql: sqltrace = yes sql: sqltracefile = "/usr/local/var/log/radius/sqltrace.sql" sql: readclients = no sql: deletestalesessions = yes sql: num_sql_socks = 5 sql: sql_user_name = "%{User-Name}" sql: default_user_profile = "" sql: query_on_not_found = no sql: authorize_check_query = "SELECT id, UserName, Attribute, Value, op FROM radcheck WHERE Username = '%{SQL-User-Name}' ORDER BY id" sql: authorize_reply_query = "SELECT id, UserName, Attribute, Value, op FROM radreply WHERE Username = '%{SQL-User-Name}' ORDER BY id" sql: authorize_group_check_query = "SELECT radgroupcheck.id, radgroupcheck.GroupName,radgroupcheck.Attribute,radgroupcheck.Value, radgroupcheck.op FROM radgroupcheck,usergroup WHERE usergroup.Username = '%{SQL-User-Name}' AND usergroup.GroupName = radgroupcheck.GroupName ORDER BY radgroupcheck.id" sql: authorize_group_reply_query = "SELECT radgroupreply.id, radgroupreply.GroupName,radgroupreply.Attribute,radgroupreply.Value, radgroupreply.op FROM radgroupreply,usergroup WHERE usergroup.Username = '%{SQL-User-Name}' AND usergroup.GroupName = radgroupreply.GroupName ORDER BY radgroupreply.id" sql: accounting_onoff_query = "UPDATE radacct SET AcctStopTime='%S', AcctSessionTime=unix_timestamp('%S') - unix_timestamp(AcctStartTime), AcctTerminateCause='%{Acct-Terminate-Cause}', AcctStopDelay = '%{Acct-Delay-Time}' WHERE AcctSessionTime=0 AND AcctStopTime=0 AND NASIPAddress= '%{NAS-IP-Address}' AND AcctStartTime <= '%S'" sql: accounting_update_query = "UPDATE radacct SET FramedIPAddress = '%{Framed-IP-Address}', AcctSessionTime = '%{Acct-Session-Time}', AcctInputOctets = '%{Acct-Input-Octets}', AcctOutputOctets = '%{Acct-Output-Octets}' WHERE AcctSessionId = '%{Acct-Session-Id}' AND UserName = '%{SQL-User-Name}' AND NASIPAddress= '%{NAS-IP-Address}'" sql: accounting_update_query_alt = "INSERT into radacct (AcctSessionId, AcctUniqueId, UserName, Realm, NASIPAddress, NASPortId, NASPortType, AcctStartTime, AcctSessionTime, AcctAuthentic, ConnectInfo_start, AcctInputOctets, AcctOutputOctets, CalledStationId, CallingStationId, ServiceType, FramedProtocol, FramedIPAddress, AcctStartDelay) values('%{Acct-Session-Id}', '%{Acct-Unique-Session-Id}', '%{SQL-User-Name}', '%{Realm}', '%{NAS-IP-Address}', '%{NAS-Port}', '%{NAS-Port-Type}', DATE_SUB('%S',INTERVAL (%{Acct-Session-Time:-0} + %{Acct-Delay-Time:-0}) SECOND), '%{Acct-Session-Time}', '%{Acct-Authentic}', '', '%{Acct-Input-Octets}', '%{Acct-Output-Octets}', '%{Called-Station-Id}', '%{Calling-Station-Id}', '%{Service-Type}', '%{Framed-Protocol}', '%{Framed-IP-Address}', '0')" sql: accounting_start_query = "INSERT into radacct (AcctSessionId, AcctUniqueId, UserName, Realm, NASIPAddress, NASPortId, NASPortType, AcctStartTime, AcctStopTime, AcctSessionTime, AcctAuthentic, ConnectInfo_start, ConnectInfo_stop, AcctInputOctets, AcctOutputOctets, CalledStationId, CallingStationId, AcctTerminateCause, ServiceType, FramedProtocol, FramedIPAddress, AcctStartDelay, AcctStopDelay) values('%{Acct-Session-Id}', '%{Acct-Unique-Session-Id}', '%{SQL-User-Name}', '%{Realm}', '%{NAS-IP-Address}', '%{NAS-Port}', '%{NAS-Port-Type}', '%S', '0', '0', '%{Acct-Authentic}', '%{Connect-Info}', '', '0', '0', '%{Called-Station-Id}', '%{Calling-Station-Id}', '', '%{Service-Type}', '%{Framed-Protocol}', '%{Framed-IP-Address}', '%{Acct-Delay-Time}', '0')" sql: accounting_start_query_alt = "UPDATE radacct SET AcctStartTime = '%S', AcctStartDelay = '%{Acct-Delay-Time}', ConnectInfo_start = '%{Connect-Info}' WHERE AcctSessionId = '%{Acct-Session-Id}' AND UserName = '%{SQL-User-Name}' AND NASIPAddress = '%{NAS-IP-Address}'" sql: accounting_stop_query = "UPDATE radacct SET AcctStopTime = '%S', AcctSessionTime = '%{Acct-Session-Time}', AcctInputOctets = '%{Acct-Input-Octets}', AcctOutputOctets = '%{Acct-Output-Octets}', AcctTerminateCause = '%{Acct-Terminate-Cause}', AcctStopDelay = '%{Acct-Delay-Time}', ConnectInfo_stop = '%{Connect-Info}' WHERE AcctSessionId = '%{Acct-Session-Id}' AND UserName = '%{SQL-User-Name}' AND NASIPAddress = '%{NAS-IP-Address}'" sql: accounting_stop_query_alt = "INSERT into radacct (AcctSessionId, AcctUniqueId, UserName, Realm, NASIPAddress, NASPortId, NASPortType, AcctStartTime, AcctStopTime, AcctSessionTime, AcctAuthentic, ConnectInfo_start, ConnectInfo_stop, AcctInputOctets, AcctOutputOctets, CalledStationId, CallingStationId, AcctTerminateCause, ServiceType, FramedProtocol, FramedIPAddress, AcctStartDelay, AcctStopDelay) values('%{Acct-Session-Id}', '%{Acct-Unique-Session-Id}', '%{SQL-User-Name}', '%{Realm}', '%{NAS-IP-Address}', '%{NAS-Port}', '%{NAS-Port-Type}', DATE_SUB('%S', INTERVAL (%{Acct-Session-Time:-0} + %{Acct-Delay-Time:-0}) SECOND), '%S', '%{Acct-Session-Time}', '%{Acct-Authentic}', '', '%{Connect-Info}', '%{Acct-Input-Octets}', '%{Acct-Output-Octets}', '%{Called-Station-Id}', '%{Calling-Station-Id}', '%{Acct-Terminate-Cause}', '%{Service-Type}', '%{Framed-Protocol}', '%{Framed-IP-Address}', '0', '%{Acct-Delay-Time}')" sql: group_membership_query = "SELECT GroupName FROM usergroup WHERE UserName='%{SQL-User-Name}'" sql: connect_failure_retry_delay = 60 sql: simul_count_query = "" sql: simul_verify_query = "SELECT RadAcctId, AcctSessionId, UserName, NASIPAddress, NASPortId, FramedIPAddress, CallingStationId, FramedProtocol FROM radacct WHERE UserName='%{SQL-User-Name}' AND AcctStopTime = 0" sql: postauth_table = "radpostauth" sql: postauth_query = "INSERT into radpostauth (id, user, pass, reply, date) values ('', '%{User-Name}', '%{User-Password:-Chap-Password}', '%{reply:Packet-Type}', NOW())" sql: safe-characters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" rlm_sql (sql): Driver rlm_sql_mysql (module rlm_sql_mysql) loaded and linked rlm_sql (sql): Attempting to connect to root@202.117.7.243:/radius rlm_sql (sql): starting 0 rlm_sql (sql): Attempting to connect rlm_sql_mysql #0 rlm_sql_mysql: Starting connect to MySQL server for #0 rlm_sql (sql): Connected new DB handle, #0 rlm_sql (sql): starting 1 rlm_sql (sql): Attempting to connect rlm_sql_mysql #1 rlm_sql_mysql: Starting connect to MySQL server for #1 rlm_sql (sql): Connected new DB handle, #1 rlm_sql (sql): starting 2 rlm_sql (sql): Attempting to connect rlm_sql_mysql #2 rlm_sql_mysql: Starting connect to MySQL server for #2 rlm_sql (sql): Connected new DB handle, #2 rlm_sql (sql): starting 3 rlm_sql (sql): Attempting to connect rlm_sql_mysql #3 rlm_sql_mysql: Starting connect to MySQL server for #3 rlm_sql (sql): Connected new DB handle, #3 rlm_sql (sql): starting 4 rlm_sql (sql): Attempting to connect rlm_sql_mysql #4 rlm_sql_mysql: Starting connect to MySQL server for #4 rlm_sql (sql): Connected new DB handle, #4 Module: Instantiated sql (sql) Module: Loaded example example: integer = 1 example: boolean = no example: string = "(null)" Module: Instantiated example (example) Module: Loaded Acct-Unique-Session-Id acct_unique: key = "User-Name, Acct-Session-Id, NAS-IP-Address, Client-IP-Address, NAS-Port" Module: Instantiated acct_unique (acct_unique) Module: Loaded detail detail: detailfile = "/usr/local/var/log/radius/radacct/%{Client-IP-Address}/detail-%Y%m%d" detail: detailperm = 384 detail: dirperm = 493 detail: locking = no Module: Instantiated detail (detail) Module: Loaded System unix: cache = no unix: passwd = "(null)" unix: shadow = "(null)" unix: group = "(null)" unix: radwtmp = "/usr/local/var/log/radius/radwtmp" unix: usegroup = no unix: cache_reload = 600 Module: Instantiated unix (unix) Module: Loaded radutmp radutmp: filename = "/usr/local/var/log/radius/radutmp" radutmp: username = "%{User-Name}" radutmp: case_sensitive = yes radutmp: check_with_nas = yes radutmp: perm = 384 radutmp: callerid = yes Module: Instantiated radutmp (radutmp) Listening on authentication *:1812 Listening on accounting *:1813 Ready to process requests. rad_recv: Access-Request packet from host 202.117.7.223:1744, id=1, length=45 User-Name = "test" X-Ascend-Pre-Input-Packets = 0x01928b5deed8b5386d303a72d838e3d254 Processing the authorize section of radiusd.conf modcall: entering group authorize for request 0 modcall[authorize]: module "preprocess" returns ok for request 0 modcall[authorize]: module "chap" returns noop for request 0 modcall[authorize]: module "mschap" returns noop for request 0 rlm_realm: No '@' in User-Name = "test", looking up realm NULL rlm_realm: No such realm "NULL" modcall[authorize]: module "suffix" returns noop for request 0 rlm_eap: No EAP-Message, not doing EAP modcall[authorize]: module "eap" returns noop for request 0 radius_xlat: 'test' rlm_sql (sql): sql_set_user escaped user --> 'test' radius_xlat: 'SELECT id, UserName, Attribute, Value, op FROM radcheck WHERE Username = 'test' ORDER BY id' rlm_sql (sql): Reserving sql socket id: 4 rlm_sql_mysql: query: SELECT id, UserName, Attribute, Value, op FROM radcheck WHERE Username = 'test' ORDER BY id radius_xlat: 'SELECT radgroupcheck.id,radgroupcheck.GroupName, radgroupcheck.Attribute,radgroupcheck.Value,radgroupcheck.op FROM radgroupcheck,usergroup WHERE usergroup.Username = 'test' AND usergroup.GroupName = radgroupcheck.GroupName ORDER BY radgroupcheck.id' rlm_sql_mysql: query: SELECT radgroupcheck.id,radgroupcheck.GroupName, radgroupcheck.Attribute,radgroupcheck.Value,radgroupcheck.op FROM radgroupcheck,usergroup WHERE usergroup.Username = 'test' AND usergroup.GroupName = radgroupcheck.GroupName ORDER BY radgroupcheck.id radius_xlat: 'SELECT id, UserName, Attribute, Value, op FROM radreply WHERE Username = 'test' ORDER BY id' rlm_sql_mysql: query: SELECT id, UserName, Attribute, Value, op FROM radreply WHERE Username = 'test' ORDER BY id radius_xlat: 'SELECT radgroupreply.id,radgroupreply.GroupName, radgroupreply.Attribute,radgroupreply.Value,radgroupreply.op FROM radgroupreply,usergroup WHERE usergroup.Username = 'test' AND usergroup.GroupName = radgroupreply.GroupName ORDER BY radgroupreply.id' rlm_sql_mysql: query: SELECT radgroupreply.id,radgroupreply.GroupName, radgroupreply.Attribute,radgroupreply.Value,radgroupreply.op FROM radgroupreply,usergroup WHERE usergroup.Username = 'test' AND usergroup.GroupName = radgroupreply.GroupName ORDER BY radgroupreply.id rlm_sql (sql): Released sql socket id: 4 modcall[authorize]: module "sql" returns ok for request 0 modcall[authorize]: module "example" returns ok for request 0 modcall: group authorize returns ok for request 0 auth: type Local auth: No User-Password or CHAP-Password attribute in the request auth: Failed to validate the user. Delaying request 0 for 1 seconds Finished request 0 Going to the next request --- Walking the entire request list --- Waking up in 1 seconds... --- Walking the entire request list --- Waking up in 1 seconds... --- Walking the entire request list --- Sending Access-Reject of id 1 to 202.117.7.223:1744 Waking up in 4 seconds... --- Walking the entire request list --- Cleaning up request 0 ID 1 with timestamp 45fa6784 Nothing to do. Sleeping until we see a request. Thanks for reply. 2007/3/14, Alan DeKok <aland@deployingradius.com>:
yao guoxian wrote:
Thanks,Alan. But I have a few questions. First, if I create a new attribute "My-Aes-Password" and include it in the Access-Requet packet, I should not include the attributes such as "User-Password" or "Chap-Password".Is it right?
Yes.
The second question is about how to write modules.
There is documentation. See "rlm_example", for one.
Sorry to ask the same question,but I want to verify my plan to see if it is pratical. The plan is as follow: I dont amend the module "rlm_chap" , I just copy all files in the ./src/modules/rlm_chap/ to a new dictory "rlm_aes" and rename files rlm_chap.* to rlm_aes.*. Then I edit rlm-chap.c to alter it to use AES to analyze the request packet. Is it pratical?
Yes.
Alan DeKok. -- http://deployingradius.com - The web site of the book http://deployingradius.com/blog/ - The blog - List info/subscribe/unsubscribe? See http://www.freeradius.org/list/users.html
yao guoxian wrote:
Sorry , Alan.
The most recently response to the "Access-request "packet from "radiusd -X" monitor are as follows( only a segment related to the request):
Look at rlm_chap for how it handles CHAP requests. You need to do something similar for your method. Alan DeKok. -- http://deployingradius.com - The web site of the book http://deployingradius.com/blog/ - The blog
Thanks,Alan. The problem seems to have been resolve. Here is anothor queston which seems unrelated to this topic.When I recompiled Freeradius,I got the following errors from the command "make" and "make install": ...... acct.c:18682:1: warning: null character(s) ignored acct.c:18682: parse error before '.' token ...... When I use vi to edit acct.c , I found the block code from line 18682 was like below: 18682 ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^ @^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ ^@^@^@^@^@^@^@^@./xlat.c^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^ @^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ 0100644^@0002216^@0002216^ @00000043641^@10304632635^@007256^@ 0^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^ @^@ustar ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^ @^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^ @^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@/* What does the codes mean? Is it possible I have changed it before and I have not noticed it ? Thanks . 2007/3/16, Alan DeKok <aland@deployingradius.com>:
yao guoxian wrote:
Sorry , Alan.
The most recently response to the "Access-request "packet from "radiusd -X" monitor are as follows( only a segment related to the request):
Look at rlm_chap for how it handles CHAP requests. You need to do something similar for your method.
Alan DeKok. -- http://deployingradius.com - The web site of the book http://deployingradius.com/blog/ - The blog - List info/subscribe/unsubscribe? See http://www.freeradius.org/list/users.html
participants (2)
-
Alan DeKok -
yao guoxian