]> git.ozlabs.org Git - ppp.git/blob - pppd/plugins/radius/sendserver.c
3612b8d57a88a8f291bd8b84635c5838f7390b09
[ppp.git] / pppd / plugins / radius / sendserver.c
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@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 }