static void radius_ip_up(void) { struct radius_attrib *attriblist, *recvattriblist; int ret; if (prev_ip_up_hook) { prev_ip_up_hook(); } if (use_account) { if (radius_server == -1) return; attriblist = NULL; recvattriblist = NULL; if (!radius_add_attrib( &attriblist, PW_VENDOR_NONE, PW_ACCT_STATUS_TYPE, PW_STATUS_START, NULL, 0)) { radius_free_attrib(attriblist); return; } sprintf(sessionid, "%x", radius_sessionid()); if (!radius_add_attrib( &attriblist, PW_VENDOR_NONE, PW_ACCT_SESSION_ID, 0, sessionid, strlen(sessionid))) { radius_free_attrib(attriblist); return; } if (!radius_common_account_attrib(&attriblist)) { radius_free_attrib(attriblist); return; } ret = radius_send_account_request( radius_server, radius_auth_port+1, radius_secret, attriblist, &recvattriblist); radius_free_attrib(attriblist); radius_free_attrib(recvattriblist); if (ret >= 0) { accountstart = 1; } } }
/* Authenticate/authorize */ static int radius_pap_auth(char *t_user, char *t_passwd, char **t_msgp, struct wordlist **t_paddrs, struct wordlist **t_popts) { int ret; struct radius_attrib *attriblist; if (!use_radius) { if (prev_pap_auth_hook) return prev_pap_auth_hook(t_user, t_passwd, t_msgp, t_paddrs, t_popts); else return -1; } *t_msgp = "Login failed"; if (radius_server == -1) { error("RADIUS: server not found"); return 0; } attriblist = NULL; if (!radius_add_attrib( &attriblist, PW_VENDOR_NONE, PW_USER_NAME, 0, t_user, strlen(t_user))) { radius_free_attrib(attriblist); return 0; } if (!radius_add_attrib( &attriblist, PW_VENDOR_NONE, PW_PASSWORD, 0, t_passwd, strlen(t_passwd))) { radius_free_attrib(attriblist); return 0; } ret = radius_auth(&attriblist, NULL); if (ret > 0) *t_msgp = "Login ok"; radius_free_attrib(attriblist); return ret; }
/* Return: success: 0, error: -1 */ static int radius_get_attrib(struct radius_attrib **list, u_char *buf, int len, char *secret, u_char *vector) { struct radius_attrib **next; next = list; *next = NULL; if (radius_get_attrib_vendor(&next, PW_VENDOR_NONE, buf, len, secret, vector) < 0) { radius_free_attrib(*list); *list = NULL; return -1; } return 0; }
static void radius_ip_down(void) { struct radius_attrib *attriblist, *recvattriblist; if (prev_ip_down_hook) { prev_ip_down_hook(); } /* Put in the accountstart check here since this hook * also gets called if an IP address could not be * negotiated. */ if (use_account && accountstart) { accountstart = 0; if (radius_server == -1) return; attriblist = NULL; recvattriblist = NULL; if (!radius_add_attrib( &attriblist, PW_VENDOR_NONE, PW_ACCT_STATUS_TYPE, PW_STATUS_STOP, NULL, 0)) { radius_free_attrib(attriblist); return; } if (!radius_add_attrib( &attriblist, PW_VENDOR_NONE, PW_ACCT_SESSION_ID, 0, sessionid, strlen(sessionid))) { radius_free_attrib(attriblist); return; } if (!radius_common_account_attrib(&attriblist)) { radius_free_attrib(attriblist); return; } if (link_stats_valid) { if (!radius_add_attrib( &attriblist, PW_VENDOR_NONE, PW_ACCT_INPUT_OCTETS, link_stats.bytes_in, NULL, 0)) { radius_free_attrib(attriblist); return; } if (!radius_add_attrib( &attriblist, PW_VENDOR_NONE, PW_ACCT_OUTPUT_OCTETS, link_stats.bytes_out, NULL, 0)) { radius_free_attrib(attriblist); return; } if (!radius_add_attrib( &attriblist, PW_VENDOR_NONE, PW_ACCT_SESSION_TIME, link_connect_time, NULL, 0)) { radius_free_attrib(attriblist); return; } } radius_send_account_request( radius_server, radius_auth_port+1, radius_secret, attriblist, &recvattriblist); radius_free_attrib(attriblist); radius_free_attrib(recvattriblist); } }
static int radius_chap_auth(char *user, u_char *remmd, int remmd_len, chap_state *cstate) { struct radius_attrib *attriblist; u_char chap_password[MAX_RESPONSE_LENGTH+1], *p; int code = CHAP_SUCCESS; if (!use_radius) { if (prev_chap_auth_hook) return prev_chap_auth_hook(user, remmd, remmd_len, cstate); else return -1; } if (radius_server == -1) { error("RADIUS: server not found"); return CHAP_FAILURE; } attriblist = NULL; if (!radius_add_attrib( &attriblist, PW_VENDOR_NONE, PW_USER_NAME, 0, user, strlen(user))) goto error; switch (cstate->chal_type) { case CHAP_DIGEST_MD5: if (remmd_len != MD5_SIGNATURE_SIZE) { error("RADIUS: invalid CHAP response length '%d'", remmd_len); goto error; } if (!radius_add_attrib( &attriblist, PW_VENDOR_NONE, PW_CHAP_CHALLENGE, 0, cstate->challenge, cstate->chal_len)) goto error; p = chap_password; *p++ = cstate->chal_id; memcpy(p, remmd, remmd_len); if (!radius_add_attrib( &attriblist, PW_VENDOR_NONE, PW_CHAP_PASSWORD, 0, chap_password, remmd_len+1)) goto error; break; #ifdef CHAPMS case CHAP_MICROSOFT: { MS_ChapResponse *response = (MS_ChapResponse *)remmd; if (remmd_len != MS_CHAP_RESPONSE_LEN) { error("RADIUS: invalid MSCHAP response length '%d'", remmd_len); goto error; } if (!radius_add_attrib( &attriblist, PW_VENDOR_MICROSOFT, PW_MS_CHAP_CHALLENGE, 0, cstate->challenge, cstate->chal_len)) goto error; p = chap_password; *p++ = cstate->chal_id; *p++ = response->UseNT; memcpy(p, response->LANManResp, sizeof(response->LANManResp)); p += sizeof(response->LANManResp); memcpy(p, response->NTResp, sizeof(response->NTResp)); p += sizeof(response->NTResp); if (!radius_add_attrib( &attriblist, PW_VENDOR_MICROSOFT, PW_MS_CHAP_RESPONSE, 0, chap_password, p-chap_password)) goto error; break; } case CHAP_MICROSOFT_V2: { MS_ChapResponse_v2 *response = (MS_ChapResponse_v2 *)remmd; if (remmd_len != MS_CHAP_RESPONSE_LEN) { error("RADIUS: invalid MSCHAPv2 response length '%d'", remmd_len); goto error; } if (!radius_add_attrib( &attriblist, PW_VENDOR_MICROSOFT, PW_MS_CHAP_CHALLENGE, 0, cstate->challenge, cstate->chal_len)) goto error; p = chap_password; *p++ = cstate->chal_id; *p++ = 0; memcpy(p, response->PeerChallenge, sizeof(response->PeerChallenge)); p += sizeof(response->PeerChallenge); memset(p, 0, sizeof(response->Reserved)); p += sizeof(response->Reserved); memcpy(p, response->NTResp, sizeof(response->NTResp)); p += sizeof(response->NTResp); if (!radius_add_attrib( &attriblist, PW_VENDOR_MICROSOFT, PW_MS_CHAP2_RESPONSE, 0, chap_password, p-chap_password)) { goto error; } code = CHAP_SUCCESS_R; break; } #endif default: error("RADIUS: unsupported challenge type '%d'", cstate->chal_type); goto error; } if (radius_auth(&attriblist, cstate) == 1) { radius_free_attrib(attriblist); return code; } error: radius_free_attrib(attriblist); return CHAP_FAILURE; }
static int radius_auth(struct radius_attrib **attriblist, chap_state *cstate) { int ret; struct radius_attrib *recvattriblist; struct radius_attrib *attrib; #ifdef CHAPMS int ms_chap2_success = 0; #endif #ifdef MPPE int mppe_send_key = 0, mppe_recv_key = 0, mppe_policy = 0, mppe_types = 0; #endif if (nas_ip_address != -1) { if (!radius_add_attrib( attriblist, PW_VENDOR_NONE, PW_NAS_IP_ADDRESS, nas_ip_address, NULL, 0)) { return 0; } } else if (nas_identifier[0]) { if (!radius_add_attrib( attriblist, PW_VENDOR_NONE, PW_NAS_IDENTIFIER, 0, nas_identifier, strlen(nas_identifier))) { return 0; } } if (nas_real_port_number != -1) { if (!radius_add_attrib( attriblist, PW_VENDOR_NONE, PW_NAS_PORT_ID, nas_real_port_number, NULL, 0)) { return 0; } } if (nas_port_type != -1) { if (!radius_add_attrib( attriblist, PW_VENDOR_NONE, PW_NAS_PORT_TYPE, nas_port_type, NULL, 0)) { return 0; } } if (!radius_add_attrib( attriblist, PW_VENDOR_NONE, PW_SERVICE_TYPE, PW_FRAMED_USER, NULL, 0)) { return 0; } if (!radius_add_attrib( attriblist, PW_VENDOR_NONE, PW_FRAMED_PROTOCOL, PW_PPP, NULL, 0)) { return 0; } if (ipcp_wantoptions[0].hisaddr) { if (!radius_add_attrib( attriblist, PW_VENDOR_NONE, PW_FRAMED_IP_ADDRESS, ntohl(ipcp_wantoptions[0].hisaddr), NULL, 0)) { return 0; } } recvattriblist = NULL; ret = radius_send_access_request( radius_server, radius_auth_port, radius_secret, *attriblist, &recvattriblist); if (ret < 0) { error("RADIUS: server failed"); ret = 0; } else if (ret == PW_AUTHENTICATION_ACK) { ret = 1; /* Default to success unless an attribute makes us fail */ for (attrib=recvattriblist; ret && attrib!=NULL; attrib=attrib->next) { if (attrib->vendor == PW_VENDOR_NONE) { switch (attrib->type) { case PW_SERVICE_TYPE: if (ntohl(attrib->u.value) != PW_FRAMED_USER) { error("RADIUS: service type '%d' is not framed", ntohl(attrib->u.value)); ret = 0; } break; case PW_FRAMED_PROTOCOL: if (ntohl(attrib->u.value) != PW_PPP) { error("RADIUS: framed protocol '%d' is not ppp", ntohl(attrib->u.value)); ret = 0; } break; case PW_FRAMED_IP_ADDRESS: if (attrib->u.value == htonl(0xffffffff)) { radius_allow_any_ip = 1; } else if (attrib->u.value == htonl(0xfffffffe)) { radius_allow_server_ip = 1; } else { radius_remote_ip_addr = attrib->u.value; } break; case PW_FRAMED_IP_NETMASK: if (attrib->u.value && attrib->u.value != 0xffffffff) { netmask = attrib->u.value; } break; case PW_FRAMED_COMPRESSION: if (ntohl(attrib->u.value) == PW_NONE) { ipcp_wantoptions[0].neg_vj = 0; ipcp_allowoptions[0].neg_vj = 0; } else if (ntohl(attrib->u.value) == PW_VAN_JACOBSEN_TCP_IP) { ipcp_wantoptions[0].neg_vj = 1; ipcp_allowoptions[0].neg_vj = 1; } break; case PW_REPLY_MESSAGE: strncpy(radius_reply_message,attrib->u.string,AUTH_STRING_LEN); radius_reply_message[AUTH_STRING_LEN] = 0; notice(radius_reply_message); break; #if 0 case PW_FRAMED_ROUTE: /* XXX: store route for adding/removing in ip-up/ip-down */ break; case PW_FRAMED_MTU: /* XXX: set the MTU? */ break; #endif case PW_IDLE_TIMEOUT: if (attrib->u.value != 0) { idle_time_limit = ntohl(attrib->u.value); } break; case PW_SESSION_TIMEOUT: if (attrib->u.value != 0) { maxconnect = ntohl(attrib->u.value); } break; case PW_CLASS: radius_class_length = attrib->length; if (radius_class_length > sizeof(radius_class)) radius_class_length = sizeof(radius_class); memcpy(radius_class, attrib->u.string, radius_class_length); break; default: notice("RADIUS: ignoring unsupported attribute %d", attrib->type); break; } } #ifdef CHAPMS else if (attrib->vendor == PW_VENDOR_MICROSOFT) { switch (attrib->type) { case PW_MS_CHAP2_SUCCESS: if (ms_chap2_success) { error("RADIUS: duplicate MS_CHAP2_SUCCESS"); ret = 0; break; } ms_chap2_success = 1; if (!cstate || (cstate->chal_type != CHAP_MICROSOFT_V2)) { error("RADIUS: unexpected MS_CHAP2_SUCCESS"); ret = 0; break; } if (attrib->length != 43) { error("RADIUS: invalid MS_CHAP2_SUCCESS length '%d'", attrib->length); ret = 0; break; } if (strncmp(attrib->u.string+1, "S=", 2)) { error("RADIUS: invalid MS_CHAP2_SUCCESS"); ret = 0; break; } memcpy(cstate->response, attrib->u.string+1, attrib->length-1); cstate->response[attrib->length-1] = '\0'; break; #ifdef MPPE case PW_MS_CHAP_MPPE_KEYS: { unsigned char Digest[SHA_DIGEST_LENGTH]; SHA_CTX Context; if (!cstate || (cstate->chal_type != CHAP_MICROSOFT)) { error("RADIUS: unexpected MS_CHAP_MPPE_KEYS"); ret = 0; break; } if (attrib->length != 32) { error("RADIUS: invalid MS_CHAP_MPPE_KEYS length '%d'", attrib->length); ret = 0; break; } memcpy(mppe_master_send_key_40, attrib->u.string, 8); memcpy(mppe_master_recv_key_40, attrib->u.string, 8); SHA1_Init(&Context); SHA1_Update(&Context, attrib->u.string + 8, 16); SHA1_Update(&Context, attrib->u.string + 8, 16); SHA1_Update(&Context, cstate->challenge, 8); SHA1_Final(Digest, &Context); memcpy(mppe_master_send_key_128, Digest, 16); memcpy(mppe_master_recv_key_128, Digest, 16); mppe_send_key = mppe_recv_key = 1; break; } case PW_MS_MPPE_SEND_KEY: if (!cstate || (cstate->chal_type != CHAP_MICROSOFT_V2)) { error("RADIUS: unexpected MS_MPPE_SEND_KEY"); ret = 0; break; } if (attrib->length != 34) { error("RADIUS: invalid MS_MPPE_SEND_KEY length '%d'", attrib->length); ret = 0; break; } if ((attrib->u.string[0] & 0x80) == 0) { error("RADIUS: invalid MS_MPPE_SEND_KEY salt '%02x%02x'", (unsigned char)attrib->u.string[0], (unsigned char)attrib->u.string[1]); ret = 0; break; } if (attrib->u.string[2] != 16) { error("RADIUS: invalid MS_MPPE_SEND_KEY keylength '%d'", attrib->u.string[2]); ret = 0; break; } memcpy(mppe_master_send_key_128, attrib->u.string+3, 16); memcpy(mppe_master_send_key_40, attrib->u.string+3, 8); mppe_send_key = 1; break; case PW_MS_MPPE_RECV_KEY: if (!cstate || (cstate->chal_type != CHAP_MICROSOFT_V2)) { error("RADIUS: unexpected MS_MPPE_RECV_KEY"); ret = 0; break; } if (attrib->length != 34) { error("RADIUS: invalid MS_MPPE_RECV_KEY length '%d'", attrib->length); ret = 0; break; } if ((attrib->u.string[0] & 0x80) == 0) { error("RADIUS: invalid MS_MPPE_RECV_KEY salt '%02x%02x'", (unsigned char)attrib->u.string[0], (unsigned char)attrib->u.string[1]); ret = 0; break; } if (attrib->u.string[2] != 16) { error("RADIUS: invalid MS_MPPE_RECV_KEY keylength '%d'", attrib->u.string[2]); ret = 0; break; } memcpy(mppe_master_recv_key_128, attrib->u.string+3, 16); memcpy(mppe_master_recv_key_40, attrib->u.string+3, 8); mppe_recv_key = 1; break; case PW_MS_MPPE_ENCRYPTION_POLICY: if (!radius_check_integer_length(attrib)) { ret = 0; break; } if (attrib->u.value == htonl(1)) { /* Encryption allowed */ ccp_allowoptions[0].mppe = 1; ccp_wantoptions[0].mppe = 0; mppe_policy = 1; } else if (attrib->u.value == htonl(2)) { /* Encryption required */ /* XXX: current version of ppd doesn't support * requiring encryption. */ ccp_allowoptions[0].mppe = ccp_wantoptions[0].mppe = 1; mppe_policy = 1; } break; case PW_MS_MPPE_ENCRYPTION_TYPES: if (!radius_check_integer_length(attrib)) { ret = 0; break; } ccp_allowoptions[0].mppe_40 = ccp_wantoptions[0].mppe_40 = (attrib->u.value & htonl(2)) ? 1 : 0; ccp_allowoptions[0].mppe_128 = ccp_wantoptions[0].mppe_128 = (attrib->u.value & htonl(4)) ? 1 : 0; mppe_types = 1; break; #endif /* MPPE */ #if 0 case PW_MS_PRIMARY_DNS_SERVER: case PW_MS_SECONDARY_DNS_SERVER: case PW_MS_PRIMARY_NBNS_SERVER: case PW_MS_SECONDARY_NBNS_SERVER: break; #endif default: notice("RADIUS: ignoring Microsoft attribute %d", attrib->type); break; } } #endif /* CHAPMS */ else { notice("RADIUS: ignoring vendor %d attribute %d", attrib->vendor, attrib->type); } } #ifdef CHAPMS if (ret && cstate && (cstate->chal_type == CHAP_MICROSOFT_V2) && !ms_chap2_success) { error("RADIUS: MSCHAPv2 success attribute not found"); ret = 0; } #endif #ifdef MPPE if (mppe_send_key && mppe_recv_key) { if (mppe_policy && mppe_types) { mppe_allowed = 1; } else if (!mppe_policy) { mppe_allowed = 1; } } #endif } else if (ret == PW_AUTHENTICATION_REJECT) { for (attrib=recvattriblist; attrib!=NULL; attrib=attrib->next) { if (attrib->vendor == PW_VENDOR_NONE && attrib->type == PW_REPLY_MESSAGE) { strncpy(radius_reply_message,attrib->u.string,AUTH_STRING_LEN); radius_reply_message[AUTH_STRING_LEN] = 0; error("%s", radius_reply_message); } } ret = 0; } else if (ret == PW_ACCESS_CHALLENGE) { error("RADIUS: server sent unexpected CHAP challenge"); ret = 0; } else { error("RADIUS: server sent unexpected response '%d'", ret); ret = 0; } radius_free_attrib(recvattriblist); return ret; }