1 /* 2 * $Id: sendserver.c,v 1.1 2004/11/14 07:26:26 paulus Exp $ 3 * 4 * Copyright (C) 1995,1996,1997 Lars Fenneberg 5 * 6 * Copyright 1992 Livingston Enterprises, Inc. 7 * 8 * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan 9 * and Merit Network, Inc. All Rights Reserved 10 * 11 * See the file COPYRIGHT for the respective terms and conditions. 12 * If the file is missing contact me at lf (at) elemental.net 13 * and I'll send you a copy. 14 * 15 */ 16 17 #include <includes.h> 18 #include <radiusclient.h> 19 #include <pathnames.h> 20 21 static void rc_random_vector (unsigned char *); 22 static int rc_check_reply (AUTH_HDR *, int, char *, unsigned char *, unsigned char); 23 24 /* 25 * Function: rc_pack_list 26 * 27 * Purpose: Packs an attribute value pair list into a buffer. 28 * 29 * Returns: Number of octets packed. 30 * 31 */ 32 33 static int rc_pack_list (VALUE_PAIR *vp, char *secret, AUTH_HDR *auth) 34 { 35 int length, i, pc, secretlen, padded_length; 36 int total_length = 0; 37 UINT4 lvalue; 38 unsigned char passbuf[MAX(AUTH_PASS_LEN, CHAP_VALUE_LENGTH)]; 39 unsigned char md5buf[256]; 40 unsigned char *buf, *vector, *lenptr; 41 42 buf = auth->data; 43 44 while (vp != (VALUE_PAIR *) NULL) 45 { 46 47 if (vp->vendorcode != VENDOR_NONE) { 48 *buf++ = PW_VENDOR_SPECIFIC; 49 50 /* Place-holder for where to put length */ 51 lenptr = buf++; 52 53 /* Insert vendor code */ 54 *buf++ = 0; 55 *buf++ = (((unsigned int) vp->vendorcode) >> 16) & 255; 56 *buf++ = (((unsigned int) vp->vendorcode) >> 8) & 255; 57 *buf++ = ((unsigned int) vp->vendorcode) & 255; 58 59 /* Insert vendor-type */ 60 *buf++ = vp->attribute; 61 62 /* Insert value */ 63 switch(vp->type) { 64 case PW_TYPE_STRING: 65 length = vp->lvalue; 66 *lenptr = length + 8; 67 *buf++ = length+2; 68 memcpy(buf, vp->strvalue, (size_t) length); 69 buf += length; 70 total_length += length+8; 71 break; 72 case PW_TYPE_INTEGER: 73 case PW_TYPE_IPADDR: 74 length = sizeof(UINT4); 75 *lenptr = length + 8; 76 *buf++ = length+2; 77 lvalue = htonl(vp->lvalue); 78 memcpy(buf, (char *) &lvalue, sizeof(UINT4)); 79 buf += length; 80 total_length += length+8; 81 break; 82 default: 83 break; 84 } 85 } else { 86 *buf++ = vp->attribute; 87 switch (vp->attribute) { 88 case PW_USER_PASSWORD: 89 90 /* Encrypt the password */ 91 92 /* Chop off password at AUTH_PASS_LEN */ 93 length = vp->lvalue; 94 if (length > AUTH_PASS_LEN) length = AUTH_PASS_LEN; 95 96 /* Calculate the padded length */ 97 padded_length = (length+(AUTH_VECTOR_LEN-1)) & ~(AUTH_VECTOR_LEN-1); 98 99 /* Record the attribute length */ 100 *buf++ = padded_length + 2; 101 102 /* Pad the password with zeros */ 103 memset ((char *) passbuf, '\0', AUTH_PASS_LEN); 104 memcpy ((char *) passbuf, vp->strvalue, (size_t) length); 105 106 secretlen = strlen (secret); 107 vector = (char *)auth->vector; 108 for(i = 0; i < padded_length; i += AUTH_VECTOR_LEN) { 109 /* Calculate the MD5 digest*/ 110 strcpy ((char *) md5buf, secret); 111 memcpy ((char *) md5buf + secretlen, vector, 112 AUTH_VECTOR_LEN); 113 rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN); 114 115 /* Remeber the start of the digest */ 116 vector = buf; 117 118 /* Xor the password into the MD5 digest */ 119 for (pc = i; pc < (i + AUTH_VECTOR_LEN); pc++) { 120 *buf++ ^= passbuf[pc]; 121 } 122 } 123 124 total_length += padded_length + 2; 125 126 break; 127 #if 0 128 case PW_CHAP_PASSWORD: 129 130 *buf++ = CHAP_VALUE_LENGTH + 2; 131 132 /* Encrypt the Password */ 133 length = vp->lvalue; 134 if (length > CHAP_VALUE_LENGTH) { 135 length = CHAP_VALUE_LENGTH; 136 } 137 memset ((char *) passbuf, '\0', CHAP_VALUE_LENGTH); 138 memcpy ((char *) passbuf, vp->strvalue, (size_t) length); 139 140 /* Calculate the MD5 Digest */ 141 secretlen = strlen (secret); 142 strcpy ((char *) md5buf, secret); 143 memcpy ((char *) md5buf + secretlen, (char *) auth->vector, 144 AUTH_VECTOR_LEN); 145 rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN); 146 147 /* Xor the password into the MD5 digest */ 148 for (i = 0; i < CHAP_VALUE_LENGTH; i++) { 149 *buf++ ^= passbuf[i]; 150 } 151 total_length += CHAP_VALUE_LENGTH + 2; 152 153 break; 154 #endif 155 default: 156 switch (vp->type) { 157 case PW_TYPE_STRING: 158 length = vp->lvalue; 159 *buf++ = length + 2; 160 memcpy (buf, vp->strvalue, (size_t) length); 161 buf += length; 162 total_length += length + 2; 163 break; 164 165 case PW_TYPE_INTEGER: 166 case PW_TYPE_IPADDR: 167 *buf++ = sizeof (UINT4) + 2; 168 lvalue = htonl (vp->lvalue); 169 memcpy (buf, (char *) &lvalue, sizeof (UINT4)); 170 buf += sizeof (UINT4); 171 total_length += sizeof (UINT4) + 2; 172 break; 173 174 default: 175 break; 176 } 177 break; 178 } 179 } 180 vp = vp->next; 181 } 182 return total_length; 183 } 184 185 /* 186 * Function: rc_send_server 187 * 188 * Purpose: send a request to a RADIUS server and wait for the reply 189 * 190 */ 191 192 int rc_send_server (SEND_DATA *data, char *msg, REQUEST_INFO *info) 193 { 194 int sockfd; 195 struct sockaddr salocal; 196 struct sockaddr saremote; 197 struct sockaddr_in *sin; 198 struct timeval authtime; 199 fd_set readfds; 200 AUTH_HDR *auth, *recv_auth; 201 UINT4 auth_ipaddr; 202 char *server_name; /* Name of server to query */ 203 int salen; 204 int result; 205 int total_length; 206 int length; 207 int retry_max; 208 int secretlen; 209 char secret[MAX_SECRET_LENGTH + 1]; 210 unsigned char vector[AUTH_VECTOR_LEN]; 211 char recv_buffer[BUFFER_LEN]; 212 char send_buffer[BUFFER_LEN]; 213 int retries; 214 VALUE_PAIR *vp; 215 216 server_name = data->server; 217 if (server_name == (char *) NULL || server_name[0] == '\0') 218 return (ERROR_RC); 219 220 if ((vp = rc_avpair_get(data->send_pairs, PW_SERVICE_TYPE)) && \ 221 (vp->lvalue == PW_ADMINISTRATIVE)) 222 { 223 strcpy(secret, MGMT_POLL_SECRET); 224 if ((auth_ipaddr = rc_get_ipaddr(server_name)) == 0) 225 return (ERROR_RC); 226 } 227 else 228 { 229 if (rc_find_server (server_name, &auth_ipaddr, secret) != 0) 230 { 231 return (ERROR_RC); 232 } 233 } 234 235 sockfd = socket (AF_INET, SOCK_DGRAM, 0); 236 if (sockfd < 0) 237 { 238 memset (secret, '\0', sizeof (secret)); 239 error("rc_send_server: socket: %s", strerror(errno)); 240 return (ERROR_RC); 241 } 242 243 length = sizeof (salocal); 244 sin = (struct sockaddr_in *) & salocal; 245 memset ((char *) sin, '\0', (size_t) length); 246 sin->sin_family = AF_INET; 247 sin->sin_addr.s_addr = htonl(INADDR_ANY); 248 sin->sin_port = htons ((unsigned short) 0); 249 if (bind (sockfd, (struct sockaddr *) sin, length) < 0 || 250 getsockname (sockfd, (struct sockaddr *) sin, &length) < 0) 251 { 252 close (sockfd); 253 memset (secret, '\0', sizeof (secret)); 254 error("rc_send_server: bind: %s: %m", server_name); 255 return (ERROR_RC); 256 } 257 258 retry_max = data->retries; /* Max. numbers to try for reply */ 259 retries = 0; /* Init retry cnt for blocking call */ 260 261 /* Build a request */ 262 auth = (AUTH_HDR *) send_buffer; 263 auth->code = data->code; 264 auth->id = data->seq_nbr; 265 266 if (data->code == PW_ACCOUNTING_REQUEST) 267 { 268 total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; 269 270 auth->length = htons ((unsigned short) total_length); 271 272 memset((char *) auth->vector, 0, AUTH_VECTOR_LEN); 273 secretlen = strlen (secret); 274 memcpy ((char *) auth + total_length, secret, secretlen); 275 rc_md5_calc (vector, (char *) auth, total_length + secretlen); 276 memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN); 277 } 278 else 279 { 280 rc_random_vector (vector); 281 memcpy (auth->vector, vector, AUTH_VECTOR_LEN); 282 283 total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; 284 285 auth->length = htons ((unsigned short) total_length); 286 } 287 288 sin = (struct sockaddr_in *) & saremote; 289 memset ((char *) sin, '\0', sizeof (saremote)); 290 sin->sin_family = AF_INET; 291 sin->sin_addr.s_addr = htonl (auth_ipaddr); 292 sin->sin_port = htons ((unsigned short) data->svc_port); 293 294 for (;;) 295 { 296 sendto (sockfd, (char *) auth, (unsigned int) total_length, (int) 0, 297 (struct sockaddr *) sin, sizeof (struct sockaddr_in)); 298 299 authtime.tv_usec = 0L; 300 authtime.tv_sec = (long) data->timeout; 301 FD_ZERO (&readfds); 302 FD_SET (sockfd, &readfds); 303 if (select (sockfd + 1, &readfds, NULL, NULL, &authtime) < 0) 304 { 305 if (errno == EINTR) 306 continue; 307 error("rc_send_server: select: %m"); 308 memset (secret, '\0', sizeof (secret)); 309 close (sockfd); 310 return (ERROR_RC); 311 } 312 if (FD_ISSET (sockfd, &readfds)) 313 break; 314 315 /* 316 * Timed out waiting for response. Retry "retry_max" times 317 * before giving up. If retry_max = 0, don't retry at all. 318 */ 319 if (++retries >= retry_max) 320 { 321 error("rc_send_server: no reply from RADIUS server %s:%u", 322 rc_ip_hostname (auth_ipaddr), data->svc_port); 323 close (sockfd); 324 memset (secret, '\0', sizeof (secret)); 325 return (TIMEOUT_RC); 326 } 327 } 328 salen = sizeof (saremote); 329 length = recvfrom (sockfd, (char *) recv_buffer, 330 (int) sizeof (recv_buffer), 331 (int) 0, &saremote, &salen); 332 333 if (length <= 0) 334 { 335 error("rc_send_server: recvfrom: %s:%d: %m", server_name,\ 336 data->svc_port); 337 close (sockfd); 338 memset (secret, '\0', sizeof (secret)); 339 return (ERROR_RC); 340 } 341 342 recv_auth = (AUTH_HDR *)recv_buffer; 343 344 result = rc_check_reply (recv_auth, BUFFER_LEN, secret, vector, data->seq_nbr); 345 346 data->receive_pairs = rc_avpair_gen(recv_auth); 347 348 close (sockfd); 349 if (info) 350 { 351 memcpy(info->secret, secret, sizeof(info->secret)); 352 memcpy(info->request_vector, vector, 353 sizeof(info->request_vector)); 354 } 355 memset (secret, '\0', sizeof (secret)); 356 357 if (result != OK_RC) return (result); 358 359 *msg = '\0'; 360 vp = data->receive_pairs; 361 while (vp) 362 { 363 if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE))) 364 { 365 strcat(msg, vp->strvalue); 366 strcat(msg, "\n"); 367 vp = vp->next; 368 } 369 } 370 371 if ((recv_auth->code == PW_ACCESS_ACCEPT) || 372 (recv_auth->code == PW_PASSWORD_ACK) || 373 (recv_auth->code == PW_ACCOUNTING_RESPONSE)) 374 { 375 result = OK_RC; 376 } 377 else 378 { 379 result = BADRESP_RC; 380 } 381 382 return (result); 383 } 384 385 /* 386 * Function: rc_check_reply 387 * 388 * Purpose: verify items in returned packet. 389 * 390 * Returns: OK_RC -- upon success, 391 * BADRESP_RC -- if anything looks funny. 392 * 393 */ 394 395 static int rc_check_reply (AUTH_HDR *auth, int bufferlen, char *secret, 396 unsigned char *vector, unsigned char seq_nbr) 397 { 398 int secretlen; 399 int totallen; 400 unsigned char calc_digest[AUTH_VECTOR_LEN]; 401 unsigned char reply_digest[AUTH_VECTOR_LEN]; 402 403 totallen = ntohs (auth->length); 404 405 secretlen = strlen (secret); 406 407 /* Do sanity checks on packet length */ 408 if ((totallen < 20) || (totallen > 4096)) 409 { 410 error("rc_check_reply: received RADIUS server response with invalid length"); 411 return (BADRESP_RC); 412 } 413 414 /* Verify buffer space, should never trigger with current buffer size and check above */ 415 if ((totallen + secretlen) > bufferlen) 416 { 417 error("rc_check_reply: not enough buffer space to verify RADIUS server response"); 418 return (BADRESP_RC); 419 } 420 /* Verify that id (seq. number) matches what we sent */ 421 if (auth->id != seq_nbr) 422 { 423 error("rc_check_reply: received non-matching id in RADIUS server response"); 424 return (BADRESP_RC); 425 } 426 427 /* Verify the reply digest */ 428 memcpy ((char *) reply_digest, (char *) auth->vector, AUTH_VECTOR_LEN); 429 memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN); 430 memcpy ((char *) auth + totallen, secret, secretlen); 431 rc_md5_calc (calc_digest, (char *) auth, totallen + secretlen); 432 433 #ifdef DIGEST_DEBUG 434 { 435 int i; 436 437 fputs("reply_digest: ", stderr); 438 for (i = 0; i < AUTH_VECTOR_LEN; i++) 439 { 440 fprintf(stderr,"%.2x ", (int) reply_digest[i]); 441 } 442 fputs("\ncalc_digest: ", stderr); 443 for (i = 0; i < AUTH_VECTOR_LEN; i++) 444 { 445 fprintf(stderr,"%.2x ", (int) calc_digest[i]); 446 } 447 fputs("\n", stderr); 448 } 449 #endif 450 451 if (memcmp ((char *) reply_digest, (char *) calc_digest, 452 AUTH_VECTOR_LEN) != 0) 453 { 454 #ifdef RADIUS_116 455 /* the original Livingston radiusd v1.16 seems to have 456 a bug in digest calculation with accounting requests, 457 authentication request are ok. i looked at the code 458 but couldn't find any bugs. any help to get this 459 kludge out are welcome. preferably i want to 460 reproduce the calculation bug here to be compatible 461 to stock Livingston radiusd v1.16. -lf, 03/14/96 462 */ 463 if (auth->code == PW_ACCOUNTING_RESPONSE) 464 return (OK_RC); 465 #endif 466 error("rc_check_reply: received invalid reply digest from RADIUS server"); 467 return (BADRESP_RC); 468 } 469 470 return (OK_RC); 471 472 } 473 474 /* 475 * Function: rc_random_vector 476 * 477 * Purpose: generates a random vector of AUTH_VECTOR_LEN octets. 478 * 479 * Returns: the vector (call by reference) 480 * 481 */ 482 483 static void rc_random_vector (unsigned char *vector) 484 { 485 int randno; 486 int i; 487 int fd; 488 489 /* well, I added this to increase the security for user passwords. 490 we use /dev/urandom here, as /dev/random might block and we don't 491 need that much randomness. BTW, great idea, Ted! -lf, 03/18/95 */ 492 493 if ((fd = open(_PATH_DEV_URANDOM, O_RDONLY)) >= 0) 494 { 495 unsigned char *pos; 496 int readcount; 497 498 i = AUTH_VECTOR_LEN; 499 pos = vector; 500 while (i > 0) 501 { 502 readcount = read(fd, (char *)pos, i); 503 pos += readcount; 504 i -= readcount; 505 } 506 507 close(fd); 508 return; 509 } /* else fall through */ 510 511 for (i = 0; i < AUTH_VECTOR_LEN;) 512 { 513 randno = magic(); 514 memcpy ((char *) vector, (char *) &randno, sizeof (int)); 515 vector += sizeof (int); 516 i += sizeof (int); 517 } 518 519 return; 520 } 521