int rc_send_server (rc_handle *rh, SEND_DATA *data, char *msg) { int sockfd; struct sockaddr_in sinlocal; struct sockaddr_in sinremote; AUTH_HDR *auth, *recv_auth; uint32_t auth_ipaddr, nas_ipaddr; char *server_name; /* Name of server to query */ socklen_t salen; int result = 0; int total_length; int length; int retry_max; size_t secretlen; char secret[MAX_SECRET_LENGTH + 1]; unsigned char vector[AUTH_VECTOR_LEN]; char recv_buffer[BUFFER_LEN]; char send_buffer[BUFFER_LEN]; int retries; VALUE_PAIR *vp; struct pollfd pfd; double start_time, timeout; server_name = data->server; if (server_name == NULL || server_name[0] == '\0') return ERROR_RC; if ((vp = rc_avpair_get(data->send_pairs, PW_SERVICE_TYPE, 0)) && \ (vp->lvalue == PW_ADMINISTRATIVE)) { strcpy(secret, MGMT_POLL_SECRET); if ((auth_ipaddr = rc_get_ipaddr(server_name)) == 0) return ERROR_RC; } else { if(data->secret != NULL) { strncpy(secret, data->secret, MAX_SECRET_LENGTH); } /* else { */ if (rc_find_server (rh, server_name, &auth_ipaddr, secret) != 0) { rc_log(LOG_ERR, "rc_send_server: unable to find server: %s", server_name); return ERROR_RC; } /*}*/ } DEBUG(LOG_ERR, "DEBUG: rc_send_server: creating socket to: %s", server_name); sockfd = socket (AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { memset (secret, '\0', sizeof (secret)); rc_log(LOG_ERR, "rc_send_server: socket: %s", strerror(errno)); return ERROR_RC; } memset((char *)&sinlocal, '\0', sizeof(sinlocal)); sinlocal.sin_family = AF_INET; sinlocal.sin_addr.s_addr = htonl(rc_own_bind_ipaddress(rh)); sinlocal.sin_port = htons((unsigned short) 0); if (bind(sockfd, SA(&sinlocal), sizeof(sinlocal)) < 0) { close (sockfd); memset (secret, '\0', sizeof (secret)); rc_log(LOG_ERR, "rc_send_server: bind: %s: %s", server_name, strerror(errno)); return ERROR_RC; } retry_max = data->retries; /* Max. numbers to try for reply */ retries = 0; /* Init retry cnt for blocking call */ memset ((char *)&sinremote, '\0', sizeof(sinremote)); sinremote.sin_family = AF_INET; sinremote.sin_addr.s_addr = htonl (auth_ipaddr); sinremote.sin_port = htons ((unsigned short) data->svc_port); /* * Fill in NAS-IP-Address (if needed) */ if (rc_avpair_get(data->send_pairs, PW_NAS_IP_ADDRESS, 0) == NULL) { if (sinlocal.sin_addr.s_addr == htonl(INADDR_ANY)) { if (rc_get_srcaddr(SA(&sinlocal), SA(&sinremote)) != 0) { close (sockfd); memset (secret, '\0', sizeof (secret)); return ERROR_RC; } } nas_ipaddr = ntohl(sinlocal.sin_addr.s_addr); rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IP_ADDRESS, &nas_ipaddr, 0, 0); } /* Build a request */ auth = (AUTH_HDR *) send_buffer; auth->code = data->code; auth->id = data->seq_nbr; if (data->code == PW_ACCOUNTING_REQUEST) { total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; auth->length = htons ((unsigned short) total_length); memset((char *) auth->vector, 0, AUTH_VECTOR_LEN); secretlen = strlen (secret); memcpy ((char *) auth + total_length, secret, secretlen); rc_md5_calc (vector, (unsigned char *) auth, total_length + secretlen); memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN); } else { rc_random_vector (vector); memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN); total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; auth->length = htons ((unsigned short) total_length); } DEBUG(LOG_ERR, "DEBUG: local %s : 0, remote %s : %u\n", inet_ntoa(sinlocal.sin_addr), inet_ntoa(sinremote.sin_addr), data->svc_port); for (;;) { sendto (sockfd, (char *) auth, (unsigned int) total_length, (int) 0, SA(&sinremote), sizeof (struct sockaddr_in)); pfd.fd = sockfd; pfd.events = POLLIN; pfd.revents = 0; start_time = rc_getctime(); for (timeout = data->timeout; timeout > 0; timeout -= rc_getctime() - start_time) { result = poll(&pfd, 1, timeout * 1000); if (result != -1 || errno != EINTR) break; } if (result == -1) { rc_log(LOG_ERR, "rc_send_server: poll: %s", strerror(errno)); memset (secret, '\0', sizeof (secret)); close (sockfd); return ERROR_RC; } if (result == 1 && (pfd.revents & POLLIN) != 0) break; /* * Timed out waiting for response. Retry "retry_max" times * before giving up. If retry_max = 0, don't retry at all. */ if (retries++ >= retry_max) { rc_log(LOG_ERR, "rc_send_server: no reply from RADIUS server %s:%u, %s", rc_ip_hostname (auth_ipaddr), data->svc_port, inet_ntoa(sinremote.sin_addr)); close (sockfd); memset (secret, '\0', sizeof (secret)); return TIMEOUT_RC; } } salen = sizeof(sinremote); length = recvfrom (sockfd, (char *) recv_buffer, (int) sizeof (recv_buffer), (int) 0, SA(&sinremote), &salen); if (length <= 0) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: %s", server_name,\ data->svc_port, strerror(errno)); close (sockfd); memset (secret, '\0', sizeof (secret)); return ERROR_RC; } recv_auth = (AUTH_HDR *)recv_buffer; if (length < AUTH_HDR_LEN || length < ntohs(recv_auth->length)) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: reply is too short", server_name, data->svc_port); close(sockfd); memset(secret, '\0', sizeof(secret)); return ERROR_RC; } result = rc_check_reply (recv_auth, BUFFER_LEN, secret, vector, data->seq_nbr); length = ntohs(recv_auth->length) - AUTH_HDR_LEN; if (length > 0) { data->receive_pairs = rc_avpair_gen(rh, NULL, recv_auth->data, length, 0); } else { data->receive_pairs = NULL; } close (sockfd); memset (secret, '\0', sizeof (secret)); if (result != OK_RC) return result; *msg = '\0'; vp = data->receive_pairs; while (vp) { if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE, 0))) { strcat(msg, vp->strvalue); strcat(msg, "\n"); vp = vp->next; } } if ((recv_auth->code == PW_ACCESS_ACCEPT) || (recv_auth->code == PW_PASSWORD_ACK) || (recv_auth->code == PW_ACCOUNTING_RESPONSE)) { result = OK_RC; } else if ((recv_auth->code == PW_ACCESS_REJECT) || (recv_auth->code == PW_PASSWORD_REJECT)) { result = REJECT_RC; } else { result = BADRESP_RC; } return result; }
/** Sends a request to a RADIUS server and waits for the reply * * @param rh a handle to parsed configuration * @param ctx if non-NULL it will contain the context of sent request; It must be released using rc_aaa_ctx_free(). * @param data a pointer to a SEND_DATA structure * @param msg must be an array of %PW_MAX_MSG_SIZE or NULL; will contain the concatenation of * any %PW_REPLY_MESSAGE received. * @param type must be %AUTH or %ACCT * @return OK_RC (0) on success, TIMEOUT_RC on timeout REJECT_RC on acess reject, or negative * on failure as return value. */ int rc_send_server_ctx(rc_handle * rh, RC_AAA_CTX ** ctx, SEND_DATA * data, char *msg, rc_type type) { int sockfd = -1; AUTH_HDR *auth, *recv_auth; char *server_name, *p; /* Name of server to query */ struct sockaddr_storage our_sockaddr; struct addrinfo *auth_addr = NULL; socklen_t salen; int result = 0; int total_length; int length, pos; int retry_max; const rc_sockets_override *sfuncs; unsigned discover_local_ip; size_t secretlen; char secret[MAX_SECRET_LENGTH + 1]; unsigned char vector[AUTH_VECTOR_LEN]; uint8_t recv_buffer[BUFFER_LEN]; uint8_t send_buffer[BUFFER_LEN]; uint8_t *attr; int retries; VALUE_PAIR *vp; struct pollfd pfd; double start_time, timeout; struct sockaddr_storage *ss_set = NULL; char *server_type = "auth"; server_name = data->server; if (server_name == NULL || server_name[0] == '\0') return ERROR_RC; if ((vp = rc_avpair_get(data->send_pairs, PW_SERVICE_TYPE, 0)) && (vp->lvalue == PW_ADMINISTRATIVE)) { strcpy(secret, MGMT_POLL_SECRET); auth_addr = rc_getaddrinfo(server_name, type == AUTH ? PW_AI_AUTH : PW_AI_ACCT); if (auth_addr == NULL) return ERROR_RC; } else { if (data->secret != NULL) { strlcpy(secret, data->secret, MAX_SECRET_LENGTH); } /* else { */ if (rc_find_server_addr (rh, server_name, &auth_addr, secret, type) != 0) { rc_log(LOG_ERR, "rc_send_server: unable to find server: %s", server_name); return ERROR_RC; } /*} */ } sfuncs = &rh->so; if (sfuncs->static_secret) { /* any static secret set in sfuncs overrides the configured */ strlcpy(secret, sfuncs->static_secret, MAX_SECRET_LENGTH); } if (sfuncs->lock) { if (sfuncs->lock(sfuncs->ptr) != 0) { rc_log(LOG_ERR, "%s: lock error", __func__); return ERROR_RC; } } rc_own_bind_addr(rh, &our_sockaddr); discover_local_ip = 0; if (our_sockaddr.ss_family == AF_INET) { if (((struct sockaddr_in *)(&our_sockaddr))->sin_addr.s_addr == INADDR_ANY) { discover_local_ip = 1; } } DEBUG(LOG_ERR, "DEBUG: rc_send_server: creating socket to: %s", server_name); if (discover_local_ip) { result = rc_get_srcaddr(SA(&our_sockaddr), auth_addr->ai_addr); if (result != 0) { memset(secret, '\0', sizeof(secret)); rc_log(LOG_ERR, "rc_send_server: cannot figure our own address"); result = ERROR_RC; goto cleanup; } } if (sfuncs->get_fd) { sockfd = sfuncs->get_fd(sfuncs->ptr, SA(&our_sockaddr)); if (sockfd < 0) { memset(secret, '\0', sizeof(secret)); rc_log(LOG_ERR, "rc_send_server: socket: %s", strerror(errno)); result = ERROR_RC; goto cleanup; } } retry_max = data->retries; /* Max. numbers to try for reply */ retries = 0; /* Init retry cnt for blocking call */ if (data->svc_port) { if (our_sockaddr.ss_family == AF_INET) ((struct sockaddr_in *)auth_addr->ai_addr)->sin_port = htons((unsigned short)data->svc_port); else ((struct sockaddr_in6 *)auth_addr->ai_addr)->sin6_port = htons((unsigned short)data->svc_port); } /* * Fill in NAS-IP-Address (if needed) */ if (rh->nas_addr_set) { rc_avpair_remove(&(data->send_pairs), PW_NAS_IP_ADDRESS, 0); rc_avpair_remove(&(data->send_pairs), PW_NAS_IPV6_ADDRESS, 0); ss_set = &rh->nas_addr; } else if (rc_avpair_get(data->send_pairs, PW_NAS_IP_ADDRESS, 0) == NULL && rc_avpair_get(data->send_pairs, PW_NAS_IPV6_ADDRESS, 0) == NULL) { ss_set = &our_sockaddr; } if (ss_set) { if (ss_set->ss_family == AF_INET) { uint32_t ip; ip = *((uint32_t *) (&((struct sockaddr_in *)ss_set)-> sin_addr)); ip = ntohl(ip); rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IP_ADDRESS, &ip, 0, 0); } else { void *p; p = &((struct sockaddr_in6 *)ss_set)->sin6_addr; rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IPV6_ADDRESS, p, 16, 0); } } /* * Fill in NAS-Identifier (if needed) */ p = rc_conf_str(rh, "nas-identifier"); if (p != NULL) { rc_avpair_remove(&(data->send_pairs), PW_NAS_IDENTIFIER, 0); rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IDENTIFIER, p, -1, 0); } /* Build a request */ auth = (AUTH_HDR *) send_buffer; auth->code = data->code; auth->id = data->seq_nbr; if (data->code == PW_ACCOUNTING_REQUEST) { server_type = "acct"; total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; auth->length = htons((unsigned short)total_length); memset((char *)auth->vector, 0, AUTH_VECTOR_LEN); secretlen = strlen(secret); memcpy((char *)auth + total_length, secret, secretlen); rc_md5_calc(vector, (unsigned char *)auth, total_length + secretlen); memcpy((char *)auth->vector, (char *)vector, AUTH_VECTOR_LEN); } else { rc_random_vector(vector); memcpy((char *)auth->vector, (char *)vector, AUTH_VECTOR_LEN); total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; auth->length = htons((unsigned short)total_length); } if (radcli_debug) { char our_addr_txt[50] = ""; /* hold a text IP */ char auth_addr_txt[50] = ""; /* hold a text IP */ getnameinfo(SA(&our_sockaddr), SS_LEN(&our_sockaddr), NULL, 0, our_addr_txt, sizeof(our_addr_txt), NI_NUMERICHOST); getnameinfo(auth_addr->ai_addr, auth_addr->ai_addrlen, NULL, 0, auth_addr_txt, sizeof(auth_addr_txt), NI_NUMERICHOST); DEBUG(LOG_ERR, "DEBUG: timeout=%d retries=%d local %s : 0, remote %s : %u\n", data->timeout, retry_max, our_addr_txt, auth_addr_txt, data->svc_port); } for (;;) { do { result = sfuncs->sendto(sfuncs->ptr, sockfd, (char *)auth, (unsigned int)total_length, (int)0, SA(auth_addr->ai_addr), auth_addr->ai_addrlen); } while (result == -1 && errno == EINTR); if (result == -1) { rc_log(LOG_ERR, "%s: socket: %s", __FUNCTION__, strerror(errno)); result = ERROR_RC; goto cleanup; } pfd.fd = sockfd; pfd.events = POLLIN; pfd.revents = 0; start_time = rc_getmtime(); for (timeout = data->timeout; timeout > 0; timeout -= rc_getmtime() - start_time) { result = poll(&pfd, 1, timeout * 1000); if (result != -1 || errno != EINTR) break; } if (result == -1) { rc_log(LOG_ERR, "rc_send_server: poll: %s", strerror(errno)); memset(secret, '\0', sizeof(secret)); SCLOSE(sockfd); result = ERROR_RC; goto cleanup; } if (result == 1 && (pfd.revents & POLLIN) != 0) { salen = auth_addr->ai_addrlen; do { length = sfuncs->recvfrom(sfuncs->ptr, sockfd, (char *)recv_buffer, (int) sizeof(recv_buffer), (int)0, SA(auth_addr-> ai_addr), &salen); } while (length == -1 && errno == EINTR); if (length <= 0) { int e = errno; rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: %s", server_name, data->svc_port, strerror(e)); if (length == -1 && (e == EAGAIN || e == EINTR)) continue; SCLOSE(sockfd); memset(secret, '\0', sizeof(secret)); result = ERROR_RC; goto cleanup; } recv_auth = (AUTH_HDR *) recv_buffer; if (length < AUTH_HDR_LEN || length < ntohs(recv_auth->length)) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: reply is too short", server_name, data->svc_port); SCLOSE(sockfd); memset(secret, '\0', sizeof(secret)); result = ERROR_RC; goto cleanup; } result = rc_check_reply(recv_auth, BUFFER_LEN, secret, vector, data->seq_nbr); if (result != BADRESPID_RC) { /* if a message that doesn't match our ID was received, then ignore * it, and try to receive more, until timeout. That is because in * DTLS the channel is shared, and we may receive duplicates or * out-of-order packets. */ break; } } /* * Timed out waiting for response. Retry "retry_max" times * before giving up. If retry_max = 0, don't retry at all. */ if (retries++ >= retry_max) { char radius_server_ip[128]; struct sockaddr_in *si = (struct sockaddr_in *)auth_addr->ai_addr; inet_ntop(auth_addr->ai_family, &si->sin_addr, radius_server_ip, sizeof(radius_server_ip)); rc_log(LOG_ERR, "rc_send_server: no reply from RADIUS %s server %s:%u", server_type, radius_server_ip, data->svc_port); SCLOSE(sockfd); memset(secret, '\0', sizeof(secret)); result = TIMEOUT_RC; goto cleanup; } } /* * If UDP is larger than RADIUS, shorten it to RADIUS. */ if (length > ntohs(recv_auth->length)) length = ntohs(recv_auth->length); /* * Verify that it's a valid RADIUS packet before doing ANYTHING with it. */ attr = recv_buffer + AUTH_HDR_LEN; while (attr < (recv_buffer + length)) { if (attr[0] == 0) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute zero is invalid", server_name, data->svc_port); SCLOSE(sockfd); memset(secret, '\0', sizeof(secret)); result = ERROR_RC; goto cleanup; } if (attr[1] < 2) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute length is too small", server_name, data->svc_port); SCLOSE(sockfd); memset(secret, '\0', sizeof(secret)); result = ERROR_RC; goto cleanup; } if ((attr + attr[1]) > (recv_buffer + length)) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute overflows the packet", server_name, data->svc_port); SCLOSE(sockfd); memset(secret, '\0', sizeof(secret)); result = ERROR_RC; goto cleanup; } attr += attr[1]; } length = ntohs(recv_auth->length) - AUTH_HDR_LEN; if (length > 0) { data->receive_pairs = rc_avpair_gen(rh, NULL, recv_auth->data, length, 0); } else { data->receive_pairs = NULL; } SCLOSE(sockfd); result = populate_ctx(ctx, secret, vector); if (result != OK_RC) { memset(secret, '\0', sizeof(secret)); goto cleanup; } memset(secret, '\0', sizeof(secret)); if (msg) { *msg = '\0'; pos = 0; vp = data->receive_pairs; while (vp) { if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE, 0))) { strappend(msg, PW_MAX_MSG_SIZE, &pos, vp->strvalue); strappend(msg, PW_MAX_MSG_SIZE, &pos, "\n"); vp = vp->next; } } } if ((recv_auth->code == PW_ACCESS_ACCEPT) || (recv_auth->code == PW_PASSWORD_ACK) || (recv_auth->code == PW_ACCOUNTING_RESPONSE)) { result = OK_RC; } else if ((recv_auth->code == PW_ACCESS_REJECT) || (recv_auth->code == PW_PASSWORD_REJECT)) { result = REJECT_RC; } else { rc_log(LOG_ERR, "rc_send_server: received RADIUS server response neither ACCEPT nor REJECT, invalid"); result = BADRESP_RC; } cleanup: if (auth_addr) freeaddrinfo(auth_addr); if (sfuncs->unlock) { if (sfuncs->unlock(sfuncs->ptr) != 0) { rc_log(LOG_ERR, "%s: unlock error", __func__); } } return result; }
/** Sends a request to a RADIUS server and waits for the reply * * @param rh a handle to parsed configuration * @param data a pointer to a #SEND_DATA structure * @param msg must be an array of %PW_MAX_MSG_SIZE or %NULL; will contain the concatenation of * any %PW_REPLY_MESSAGE received. * @param flags must be %AUTH or %ACCT * @return %OK_RC (0) on success, %TIMEOUT_RC on timeout %REJECT_RC on acess reject, or negative * on failure as return value. */ int rc_send_server (rc_handle *rh, SEND_DATA *data, char *msg, unsigned flags) { int sockfd; AUTH_HDR *auth, *recv_auth; char *server_name; /* Name of server to query */ struct sockaddr_storage our_sockaddr; struct addrinfo *auth_addr = NULL; socklen_t salen; int result = 0; int total_length; int length, pos; int retry_max; unsigned discover_local_ip; size_t secretlen; char secret[MAX_SECRET_LENGTH + 1]; unsigned char vector[AUTH_VECTOR_LEN]; uint8_t recv_buffer[BUFFER_LEN]; uint8_t send_buffer[BUFFER_LEN]; char our_addr_txt[50]; /* hold a text IP */ char auth_addr_txt[50]; /* hold a text IP */ uint8_t *attr; int retries; VALUE_PAIR *vp; struct pollfd pfd; double start_time, timeout; server_name = data->server; if (server_name == NULL || server_name[0] == '\0') return ERROR_RC; if ((vp = rc_avpair_get(data->send_pairs, PW_SERVICE_TYPE, 0)) && \ (vp->lvalue == PW_ADMINISTRATIVE)) { strcpy(secret, MGMT_POLL_SECRET); auth_addr = rc_getaddrinfo(server_name, flags==AUTH?PW_AI_AUTH:PW_AI_ACCT); if (auth_addr == NULL) return ERROR_RC; } else { if(data->secret != NULL) { strlcpy(secret, data->secret, MAX_SECRET_LENGTH); } /* else { */ if (rc_find_server_addr (rh, server_name, &auth_addr, secret, flags) != 0) { rc_log(LOG_ERR, "rc_send_server: unable to find server: %s", server_name); return ERROR_RC; } /*}*/ } rc_own_bind_addr(rh, &our_sockaddr); discover_local_ip = 0; if (our_sockaddr.ss_family == AF_INET) { if (((struct sockaddr_in*)(&our_sockaddr))->sin_addr.s_addr == INADDR_ANY) { discover_local_ip = 1; } } DEBUG(LOG_ERR, "DEBUG: rc_send_server: creating socket to: %s", server_name); if (discover_local_ip) { result = rc_get_srcaddr(SA(&our_sockaddr), auth_addr->ai_addr); if (result != 0) { memset (secret, '\0', sizeof (secret)); rc_log(LOG_ERR, "rc_send_server: cannot figure our own address"); result = ERROR_RC; goto cleanup; } } sockfd = socket (our_sockaddr.ss_family, SOCK_DGRAM, 0); if (sockfd < 0) { memset (secret, '\0', sizeof (secret)); rc_log(LOG_ERR, "rc_send_server: socket: %s", strerror(errno)); result = ERROR_RC; goto cleanup; } if (our_sockaddr.ss_family == AF_INET) ((struct sockaddr_in*)&our_sockaddr)->sin_port = 0; else ((struct sockaddr_in6*)&our_sockaddr)->sin6_port = 0; if (bind(sockfd, SA(&our_sockaddr), SS_LEN(&our_sockaddr)) < 0) { close (sockfd); memset (secret, '\0', sizeof (secret)); rc_log(LOG_ERR, "rc_send_server: bind: %s: %s", server_name, strerror(errno)); result = ERROR_RC; goto cleanup; } retry_max = data->retries; /* Max. numbers to try for reply */ retries = 0; /* Init retry cnt for blocking call */ if (data->svc_port) { if (our_sockaddr.ss_family == AF_INET) ((struct sockaddr_in*)auth_addr->ai_addr)->sin_port = htons ((unsigned short) data->svc_port); else ((struct sockaddr_in6*)auth_addr->ai_addr)->sin6_port = htons ((unsigned short) data->svc_port); } /* * Fill in NAS-IP-Address (if needed) */ if (rc_avpair_get(data->send_pairs, PW_NAS_IP_ADDRESS, 0) == NULL && rc_avpair_get(data->send_pairs, PW_NAS_IPV6_ADDRESS, 0) == NULL) { if (our_sockaddr.ss_family == AF_INET) { uint32_t ip; ip = *((uint32_t*)(&((struct sockaddr_in*)&our_sockaddr)->sin_addr)); ip = ntohl(ip); rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IP_ADDRESS, &ip, 0, 0); } else { void *p; p = &((struct sockaddr_in6*)&our_sockaddr)->sin6_addr; rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IPV6_ADDRESS, p, 0, 0); } } /* Build a request */ auth = (AUTH_HDR *) send_buffer; auth->code = data->code; auth->id = data->seq_nbr; if (data->code == PW_ACCOUNTING_REQUEST) { total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; auth->length = htons ((unsigned short) total_length); memset((char *) auth->vector, 0, AUTH_VECTOR_LEN); secretlen = strlen (secret); memcpy ((char *) auth + total_length, secret, secretlen); rc_md5_calc (vector, (unsigned char *) auth, total_length + secretlen); memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN); } else { rc_random_vector (vector); memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN); total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; auth->length = htons ((unsigned short) total_length); } getnameinfo(SA(&our_sockaddr), SS_LEN(&our_sockaddr), NULL, 0, our_addr_txt, sizeof(our_addr_txt), NI_NUMERICHOST); getnameinfo(auth_addr->ai_addr, auth_addr->ai_addrlen, NULL, 0, auth_addr_txt, sizeof(auth_addr_txt), NI_NUMERICHOST); DEBUG(LOG_ERR, "DEBUG: local %s : 0, remote %s : %u\n", our_addr_txt, auth_addr_txt, data->svc_port); for (;;) { do { result = sendto (sockfd, (char *) auth, (unsigned int)total_length, (int) 0, SA(auth_addr->ai_addr), auth_addr->ai_addrlen); } while (result == -1 && errno == EINTR); if (result == -1) { rc_log(LOG_ERR, "%s: socket: %s", __FUNCTION__, strerror(errno)); } pfd.fd = sockfd; pfd.events = POLLIN; pfd.revents = 0; if(data->timeout_ms > 0){ start_time = rc_getctime_ms(); for (timeout = data->timeout_ms; timeout > 0;timeout -= rc_getctime_ms() - start_time) { result = poll(&pfd, 1, timeout ); if (result != -1 || errno != EINTR) break; } }else{ start_time = rc_getctime(); for (timeout = data->timeout; timeout > 0;timeout -= rc_getctime() - start_time) { result = poll(&pfd, 1, timeout* 1000); if (result != -1 || errno != EINTR) break; } } if (result == -1) { rc_log(LOG_ERR, "rc_send_server: poll: %s", strerror(errno)); memset (secret, '\0', sizeof (secret)); close (sockfd); result = ERROR_RC; goto cleanup; } if (result == 1 && (pfd.revents & POLLIN) != 0) break; /* * Timed out waiting for response. Retry "retry_max" times * before giving up. If retry_max = 0, don't retry at all. */ if (retries++ >= retry_max) { rc_log(LOG_ERR, "rc_send_server: no reply from RADIUS server %s:%u", auth_addr_txt, data->svc_port); close (sockfd); memset (secret, '\0', sizeof (secret)); result = TIMEOUT_RC; goto cleanup; } } salen = auth_addr->ai_addrlen; do { length = recvfrom (sockfd, (char *) recv_buffer, (int) sizeof (recv_buffer), (int) 0, SA(auth_addr->ai_addr), &salen); } while(length == -1 && errno == EINTR); if (length <= 0) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: %s", server_name,\ data->svc_port, strerror(errno)); close (sockfd); memset (secret, '\0', sizeof (secret)); result = ERROR_RC; goto cleanup; } recv_auth = (AUTH_HDR *)recv_buffer; if (length < AUTH_HDR_LEN || length < ntohs(recv_auth->length)) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: reply is too short", server_name, data->svc_port); close(sockfd); memset(secret, '\0', sizeof(secret)); result = ERROR_RC; goto cleanup; } /* * If UDP is larger than RADIUS, shorten it to RADIUS. */ if (length > ntohs(recv_auth->length)) length = ntohs(recv_auth->length); /* * Verify that it's a valid RADIUS packet before doing ANYTHING with it. */ attr = recv_buffer + AUTH_HDR_LEN; while (attr < (recv_buffer + length)) { if (attr[0] == 0) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute zero is invalid", server_name, data->svc_port); close(sockfd); memset(secret, '\0', sizeof(secret)); return ERROR_RC; } if (attr[1] < 2) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute length is too small", server_name, data->svc_port); close(sockfd); memset(secret, '\0', sizeof(secret)); return ERROR_RC; } if ((attr + attr[1]) > (recv_buffer + length)) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute overflows the packet", server_name, data->svc_port); close(sockfd); memset(secret, '\0', sizeof(secret)); return ERROR_RC; } attr += attr[1]; } result = rc_check_reply (recv_auth, BUFFER_LEN, secret, vector, data->seq_nbr); length = ntohs(recv_auth->length) - AUTH_HDR_LEN; if (length > 0) { data->receive_pairs = rc_avpair_gen(rh, NULL, recv_auth->data, length, 0); } else { data->receive_pairs = NULL; } close (sockfd); memset (secret, '\0', sizeof (secret)); if (result != OK_RC) { goto cleanup; } if (msg) { *msg = '\0'; pos = 0; vp = data->receive_pairs; while (vp) { if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE, 0))) { strappend(msg, PW_MAX_MSG_SIZE, &pos, vp->strvalue); strappend(msg, PW_MAX_MSG_SIZE, &pos, "\n"); vp = vp->next; } } } if ((recv_auth->code == PW_ACCESS_ACCEPT) || (recv_auth->code == PW_PASSWORD_ACK) || (recv_auth->code == PW_ACCOUNTING_RESPONSE)) { result = OK_RC; } else if ((recv_auth->code == PW_ACCESS_REJECT) || (recv_auth->code == PW_PASSWORD_REJECT)) { result = REJECT_RC; } else { result = BADRESP_RC; } cleanup: if (auth_addr) freeaddrinfo(auth_addr); return result; }