static void radius_test(struct parse_result *res) { struct addrinfo hints, *ai; int sock, retval; struct sockaddr_storage sockaddr; socklen_t sockaddrlen; RADIUS_PACKET *reqpkt, *respkt; struct sockaddr_in *sin4; struct sockaddr_in6 *sin6; uint32_t u32val; uint8_t id; reqpkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST); if (reqpkt == NULL) err(1, "radius_new_request_packet"); id = arc4random(); radius_set_id(reqpkt, id); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; retval = getaddrinfo(res->hostname, "radius", &hints, &ai); if (retval) errx(1, "%s %s", res->hostname, gai_strerror(retval)); if (res->port != 0) ((struct sockaddr_in *)ai->ai_addr)->sin_port = htons(res->port); sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock == -1) err(1, "socket"); /* Prepare NAS-IP{,V6}-ADDRESS attribute */ if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) err(1, "connect"); sockaddrlen = sizeof(sockaddr); if (getsockname(sock, (struct sockaddr *)&sockaddr, &sockaddrlen) == -1) err(1, "getsockname"); sin4 = (struct sockaddr_in *)&sockaddr; sin6 = (struct sockaddr_in6 *)&sockaddr; switch (sockaddr.ss_family) { case AF_INET: radius_put_ipv4_attr(reqpkt, RADIUS_TYPE_NAS_IP_ADDRESS, sin4->sin_addr); break; case AF_INET6: radius_put_raw_attr(reqpkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, sin6->sin6_addr.s6_addr, sizeof(sin6->sin6_addr.s6_addr)); break; } /* User-Name and User-Password */ radius_put_string_attr(reqpkt, RADIUS_TYPE_USER_NAME, res->username); switch (res->auth_method) { case PAP: if (res->password != NULL) radius_put_user_password_attr(reqpkt, res->password, res->secret); break; case CHAP: { u_char chal[16]; u_char resp[1 + MD5_DIGEST_LENGTH]; /* "1 + " for CHAP Id */ MD5_CTX md5ctx; arc4random_buf(resp, 1); /* CHAP Id is random */ MD5Init(&md5ctx); MD5Update(&md5ctx, resp, 1); if (res->password != NULL) MD5Update(&md5ctx, res->password, strlen(res->password)); MD5Update(&md5ctx, chal, sizeof(chal)); MD5Final(resp + 1, &md5ctx); radius_put_raw_attr(reqpkt, RADIUS_TYPE_CHAP_CHALLENGE, chal, sizeof(chal)); radius_put_raw_attr(reqpkt, RADIUS_TYPE_CHAP_PASSWORD, resp, sizeof(resp)); } break; case MSCHAPV2: { u_char pass[256], chal[16]; u_int i, lpass; struct _resp { u_int8_t ident; u_int8_t flags; char peer_challenge[16]; char reserved[8]; char response[24]; } __packed resp; if (res->password == NULL) { lpass = 0; } else { lpass = strlen(res->password); if (lpass * 2 >= sizeof(pass)) err(1, "password too long"); for (i = 0; i < lpass; i++) { pass[i * 2] = res->password[i]; pass[i * 2 + 1] = 0; } } memset(&resp, 0, sizeof(resp)); resp.ident = arc4random(); arc4random_buf(chal, sizeof(chal)); arc4random_buf(resp.peer_challenge, sizeof(resp.peer_challenge)); mschap_nt_response(chal, resp.peer_challenge, (char *)res->username, strlen(res->username), pass, lpass * 2, resp.response); radius_put_vs_raw_attr(reqpkt, RADIUS_VENDOR_MICROSOFT, RADIUS_VTYPE_MS_CHAP_CHALLENGE, chal, sizeof(chal)); radius_put_vs_raw_attr(reqpkt, RADIUS_VENDOR_MICROSOFT, RADIUS_VTYPE_MS_CHAP2_RESPONSE, &resp, sizeof(resp)); explicit_bzero(pass, sizeof(pass)); } break; } u32val = htonl(res->nas_port); radius_put_raw_attr(reqpkt, RADIUS_TYPE_NAS_PORT, &u32val, 4); radius_put_message_authenticator(reqpkt, res->secret); /* Send! */ fprintf(stderr, "Sending:\n"); radius_dump(stdout, reqpkt, false, res->secret); if (send(sock, radius_get_data(reqpkt), radius_get_length(reqpkt), 0) == -1) warn("send"); if ((respkt = radius_recv(sock, 0)) == NULL) warn("recv"); else { radius_set_request_packet(respkt, reqpkt); fprintf(stderr, "\nReceived:\n"); radius_dump(stdout, respkt, true, res->secret); } /* Release the resources */ radius_delete_packet(reqpkt); if (respkt) radius_delete_packet(respkt); close(sock); freeaddrinfo(ai); explicit_bzero((char *)res->secret, strlen(res->secret)); if (res->password) explicit_bzero((char *)res->password, strlen(res->password)); return; }
/* Return: success: length, error: -1 */ static int radius_send( u_long host, int port, char *secret, u_char *sendbuf, int sendlen, u_char *recvbuf, int maxrecvlen) { int s; struct sockaddr_in salocal, saremote; fd_set set; struct timeval timeout; int ret, recvlen, sendcount; s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { syslog(LOG_ERR, "RADIUS: open socket failed: %m"); return -1; } memset(&salocal, 0, sizeof(salocal)); salocal.sin_family = AF_INET; salocal.sin_addr.s_addr = htonl(INADDR_ANY); salocal.sin_port = 0; if (bind(s, (struct sockaddr*)&salocal, sizeof(salocal)) < 0) { syslog(LOG_ERR, "RADIUS: bind socket failed: %m"); close(s); return -1; } memset(&saremote, 0, sizeof(saremote)); saremote.sin_family = AF_INET; saremote.sin_addr.s_addr = htonl(host); saremote.sin_port = htons(port); if (sendto(s, sendbuf, sendlen, 0, (struct sockaddr*)&saremote, sizeof(saremote)) < 0) { syslog(LOG_ERR, "RADIUS: sendto failed: %m"); close(s); return -1; } sendcount = 1; while (sendcount < 10) { FD_ZERO(&set); FD_SET(s, &set); timeout.tv_sec = RESEND_TIMEOUT; timeout.tv_usec = 0; ret = select(s+1, &set, NULL, NULL, &timeout); if (ret < 0) { syslog(LOG_ERR, "RADIUS: select failed: %m"); close(s); return -1; } if (ret == 0) { /* Timed out so resend */ if (sendcount > 3) { syslog(LOG_WARNING, "RADIUS: server %s not responding", inet_ntoa(saremote.sin_addr)); } if (sendto(s, sendbuf, sendlen, 0, (struct sockaddr*)&saremote, sizeof(saremote)) < 0) { syslog(LOG_ERR, "RADIUS: sendto failed: %m"); close(s); return -1; } sendcount++; } else if (FD_ISSET(s, &set)) { recvlen = radius_recv(s, secret, sendbuf, recvbuf, maxrecvlen, &saremote); if (recvlen != 0) { /* either -1 , or positive */ close(s); return recvlen; } } } close(s); syslog(LOG_ERR, "RADIUS: maximum retries reached for server %s", inet_ntoa(saremote.sin_addr)); return -1; }