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