]> git.ozlabs.org Git - ppp.git/blob - pppd/plugins/radius/radiusclient/lib/avpair.c
Add these files, used with TDB.
[ppp.git] / pppd / plugins / radius / radiusclient / lib / avpair.c
1 /*
2  * $Id: avpair.c,v 1.4 2003/04/16 05:55:35 fcusack Exp $
3  *
4  * Copyright (C) 1995 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
21 static void rc_extract_vendor_specific_attributes(int attrlen,
22                                                   unsigned char *ptr,
23                                                   VALUE_PAIR **vp);
24 /*
25  * Function: rc_avpair_add
26  *
27  * Purpose: add an attribute-value pair to the given list.
28  *
29  * Returns: pointer to added a/v pair upon success, NULL pointer upon failure.
30  *
31  * Remarks: Always appends the new pair to the end of the list.
32  *
33  */
34
35 VALUE_PAIR *rc_avpair_add (VALUE_PAIR **list, int attrid, void *pval, int len,
36                            int vendorcode)
37 {
38         VALUE_PAIR     *vp;
39
40         vp = rc_avpair_new (attrid, pval, len, vendorcode);
41
42         if (vp != (VALUE_PAIR *) NULL)
43         {
44                 rc_avpair_insert (list, (VALUE_PAIR *) NULL, vp);
45         }
46
47         return vp;
48
49 }
50
51 /*
52  * Function: rc_avpair_assign
53  *
54  * Purpose: assign the given value to an attribute-value pair.
55  *
56  * Returns:  0 on success,
57  *          -1 on failure.
58  *
59  */
60
61 int rc_avpair_assign (VALUE_PAIR *vp, void *pval, int len)
62 {
63         int     result = -1;
64
65         switch (vp->type)
66         {
67                 case PW_TYPE_STRING:
68
69                         if (((len == 0) && (strlen ((char *) pval)) > AUTH_STRING_LEN)
70                             || (len > AUTH_STRING_LEN)) {
71                                 rc_log(LOG_ERR, "rc_avpair_assign: bad attribute length");
72                                 return result;
73                     }
74
75                         if (len > 0) {
76                                 memcpy(vp->strvalue, (char *)pval, len);
77                                 vp->strvalue[len] = '\0';
78                                 vp->lvalue = len;
79                         } else {
80                         strncpy (vp->strvalue, (char *) pval, AUTH_STRING_LEN);
81                         vp->lvalue = strlen((char *) pval);
82                         }
83
84                         result = 0;
85                         break;
86
87                 case PW_TYPE_DATE:
88                 case PW_TYPE_INTEGER:
89                 case PW_TYPE_IPADDR:
90
91                         vp->lvalue = * (UINT4 *) pval;
92
93                         result = 0;
94                         break;
95
96                 default:
97                         rc_log(LOG_ERR, "rc_avpair_assign: unknown attribute %d", vp->type);
98         }
99         return result;
100 }
101
102 /*
103  * Function: rc_avpair_new
104  *
105  * Purpose: make a new attribute-value pair with given parameters.
106  *
107  * Returns: pointer to generated a/v pair when successful, NULL when failure.
108  *
109  */
110
111 VALUE_PAIR *rc_avpair_new (int attrid, void *pval, int len, int vendorcode)
112 {
113         VALUE_PAIR     *vp = (VALUE_PAIR *) NULL;
114         DICT_ATTR      *pda;
115
116         if ((pda = rc_dict_getattr (attrid, vendorcode)) == (DICT_ATTR *) NULL)
117         {
118                 rc_log(LOG_ERR,"rc_avpair_new: unknown attribute %d", attrid);
119         }
120         else
121         {
122                 if ((vp = (VALUE_PAIR *) malloc (sizeof (VALUE_PAIR)))
123                                                         != (VALUE_PAIR *) NULL)
124                 {
125                         strncpy (vp->name, pda->name, sizeof (vp->name));
126                         vp->attribute = attrid;
127                         vp->vendorcode = vendorcode;
128                         vp->next = (VALUE_PAIR *) NULL;
129                         vp->type = pda->type;
130                         if (rc_avpair_assign (vp, pval, len) == 0)
131                         {
132                                 return vp;
133                         }
134                         free (vp);
135                         vp = (VALUE_PAIR *) NULL;
136                 }
137                 else
138                 {
139                         rc_log(LOG_CRIT,"rc_avpair_new: out of memory");
140                 }
141         }
142         return vp;
143 }
144
145 /*
146  *
147  * Function: rc_avpair_gen
148  *
149  * Purpose: takes attribute/value pairs from buffer and builds a
150  *          value_pair list using allocated memory.
151  *
152  * Returns: value_pair list or NULL on failure
153  */
154
155 VALUE_PAIR *rc_avpair_gen (AUTH_HDR *auth)
156 {
157         int             length;
158         int             x_len;
159         int             attribute;
160         int             attrlen;
161         UINT4           lvalue;
162         unsigned char         *x_ptr;
163         unsigned char         *ptr;
164         DICT_ATTR      *attr;
165         VALUE_PAIR     *vp;
166         VALUE_PAIR     *pair;
167         unsigned char   hex[3];         /* For hex string conversion. */
168         char            buffer[512];
169
170         /*
171          * Extract attribute-value pairs
172          */
173         ptr = auth->data;
174         length = ntohs ((unsigned short) auth->length) - AUTH_HDR_LEN;
175         vp = (VALUE_PAIR *) NULL;
176
177         while (length > 0)
178         {
179                 attribute = *ptr++;
180                 attrlen = *ptr++;
181                 attrlen -= 2;
182                 if (attrlen < 0)
183                 {
184                         rc_log(LOG_ERR, "rc_avpair_gen: received attribute with invalid length");
185                         break;
186                 }
187
188                 /* Handle vendor-specific specially */
189                 if (attribute == PW_VENDOR_SPECIFIC) {
190                     rc_extract_vendor_specific_attributes(attrlen, ptr, &vp);
191                     ptr += attrlen;
192                     length -= (attrlen + 2);
193                     continue;
194                 }
195                 if ((attr = rc_dict_getattr (attribute, VENDOR_NONE)) == (DICT_ATTR *) NULL)
196                 {
197                         *buffer= '\0';  /* Initial length. */
198                         for (x_ptr = ptr, x_len = attrlen ;
199                                 x_len > 0 ;
200                                 x_len--, x_ptr++)
201                         {
202                                 sprintf (hex, "%2.2X", *x_ptr);
203                                 strcat (buffer, hex);
204                         }
205                         rc_log(LOG_WARNING, "rc_avpair_gen: received unknown attribute %d of length %d: 0x%s",
206                                 attribute, attrlen, buffer);
207                 }
208                 else
209                 {
210                         if ((pair =
211                                 (VALUE_PAIR *) malloc (sizeof (VALUE_PAIR))) ==
212                                         (VALUE_PAIR *) NULL)
213                         {
214                                 rc_log(LOG_CRIT, "rc_avpair_gen: out of memory");
215                                 rc_avpair_free(vp);
216                                 return NULL;
217                         }
218                         strcpy (pair->name, attr->name);
219                         pair->attribute = attr->value;
220                         pair->vendorcode = VENDOR_NONE;
221                         pair->type = attr->type;
222                         pair->next = (VALUE_PAIR *) NULL;
223
224                         switch (attr->type)
225                         {
226
227                             case PW_TYPE_STRING:
228                                 memcpy (pair->strvalue, (char *) ptr, (size_t) attrlen);
229                                 pair->strvalue[attrlen] = '\0';
230                                 pair->lvalue = attrlen;
231                                 rc_avpair_insert (&vp, (VALUE_PAIR *) NULL, pair);
232                                 break;
233
234                             case PW_TYPE_INTEGER:
235                             case PW_TYPE_IPADDR:
236                                 memcpy ((char *) &lvalue, (char *) ptr,
237                                         sizeof (UINT4));
238                                 pair->lvalue = ntohl (lvalue);
239                                 rc_avpair_insert (&vp, (VALUE_PAIR *) NULL, pair);
240                                 break;
241
242                             default:
243                                 rc_log(LOG_WARNING, "rc_avpair_gen: %s has unknown type", attr->name);
244                                 free (pair);
245                                 break;
246                         }
247
248                 }
249                 ptr += attrlen;
250                 length -= attrlen + 2;
251         }
252         return (vp);
253 }
254
255 /*
256  * Function: rc_extract_vendor_specific_attributes
257  *
258  * Purpose: Extracts vendor-specific attributes, assuming they are in
259  *          the "SHOULD" format recommended by RCF 2138.
260  *
261  * Returns: found value_pair
262  *
263  */
264 static void rc_extract_vendor_specific_attributes(int attrlen,
265                                                   unsigned char *ptr,
266                                                   VALUE_PAIR **vp)
267 {
268     int vendor_id;
269     int vtype;
270     int vlen;
271     UINT4 lvalue;
272     DICT_ATTR *attr;
273     VALUE_PAIR *pair;
274
275     /* ptr is sitting at vendor-ID */
276     if (attrlen < 8) {
277         /* Nothing to see here... */
278         return;
279     }
280
281     /* High-order octet of Vendor-Id must be zero (RFC2138) */
282     if (*ptr) {
283         return;
284     }
285
286     /* Extract vendor_id */
287     vendor_id = (int) (
288         ((unsigned int) ptr[1]) * 256 * 256 +
289         ((unsigned int) ptr[2]) * 256 +
290         ((unsigned int) ptr[3]));
291     /* Bump ptr up to contents */
292     ptr += 4;
293
294     /* Set attrlen to length of data */
295     attrlen -= 4;
296     for (; attrlen; attrlen -= vlen+2, ptr += vlen) {
297         vtype = *ptr++;
298         vlen = *ptr++;
299         vlen -= 2;
300         if (vlen < 0 || vlen > attrlen - 2) {
301             /* Do not log an error.  We are supposed to be able to cope with
302                arbitrary vendor-specific gunk */
303             return;
304         }
305         /* Looks plausible... */
306         if ((attr = rc_dict_getattr(vtype, vendor_id)) == NULL) {
307             continue;
308         }
309
310         /* TODO: Check that length matches data size!!!!! */
311         pair = (VALUE_PAIR *) malloc(sizeof(VALUE_PAIR));
312         if (!pair) {
313             rc_log(LOG_CRIT, "rc_avpair_gen: out of memory");
314             return;
315         }
316         strcpy(pair->name, attr->name);
317         pair->attribute = attr->value;
318         pair->vendorcode = vendor_id;
319         pair->type = attr->type;
320         pair->next = NULL;
321         switch (attr->type) {
322         case PW_TYPE_STRING:
323             memcpy (pair->strvalue, (char *) ptr, (size_t) vlen);
324             pair->strvalue[vlen] = '\0';
325             pair->lvalue = vlen;
326             rc_avpair_insert (vp, (VALUE_PAIR *) NULL, pair);
327             break;
328
329         case PW_TYPE_INTEGER:
330         case PW_TYPE_IPADDR:
331             memcpy ((char *) &lvalue, (char *) ptr,
332                     sizeof (UINT4));
333             pair->lvalue = ntohl (lvalue);
334             rc_avpair_insert (vp, (VALUE_PAIR *) NULL, pair);
335             break;
336
337         default:
338             rc_log(LOG_WARNING, "rc_avpair_gen: %s has unknown type", attr->name);
339             free (pair);
340             break;
341         }
342     }
343 }
344
345 /*
346  * Function: rc_avpair_get
347  *
348  * Purpose: Find the first attribute value-pair (which matches the given
349  *          attribute) from the specified value-pair list.
350  *
351  * Returns: found value_pair
352  *
353  */
354
355 VALUE_PAIR *rc_avpair_get (VALUE_PAIR *vp, UINT4 attr)
356 {
357         for (; vp != (VALUE_PAIR *) NULL && vp->attribute != attr; vp = vp->next)
358         {
359                 continue;
360         }
361         return (vp);
362 }
363
364 /*
365  * Function: rc_avpair_copy
366  *
367  * Purpose: Return a copy of the existing list "p" ala strdup().
368  *
369  */
370 VALUE_PAIR *rc_avpair_copy(VALUE_PAIR *p)
371 {
372         VALUE_PAIR *vp, *fp = NULL, *lp = NULL;
373
374         while (p) {
375                 vp = malloc(sizeof(VALUE_PAIR));
376                 if (!vp) {
377                     rc_log(LOG_CRIT, "rc_avpair_copy: out of memory");
378                     return NULL; /* leaks a little but so what */
379                 }
380                 *vp = *p;
381                 if (!fp)
382                         fp = vp;
383                 if (lp)
384                         lp->next = vp;
385                 lp = vp;
386                 p = p->next;
387         }
388
389         return fp;
390 }
391
392 /*
393  * Function: rc_avpair_insert
394  *
395  * Purpose: Given the address of an existing list "a" and a pointer
396  *          to an entry "p" in that list, add the list "b" to
397  *          the "a" list after the "p" entry.  If "p" is NULL, add
398  *          the list "b" to the end of "a".
399  *
400  */
401
402 void rc_avpair_insert (VALUE_PAIR **a, VALUE_PAIR *p, VALUE_PAIR *b)
403 {
404         VALUE_PAIR     *this_node = NULL;
405         VALUE_PAIR     *vp;
406
407         if (*a == (VALUE_PAIR *) NULL)
408         {
409                 *a = b;
410                 return;
411         }
412
413         if (!b)
414                 return;
415
416         vp = *a;
417
418         if ( p == (VALUE_PAIR *) NULL) /* run to end of "a" list */
419         {
420                 while (vp != (VALUE_PAIR *) NULL)
421                 {
422                         this_node = vp;
423                         vp = vp->next;
424                 }
425         }
426         else /* look for the "p" entry in the "a" list (or run to end) */
427         {
428                 this_node = *a;
429                 while (this_node != (VALUE_PAIR *) NULL)
430                 {
431                         if (this_node == p)
432                         {
433                                 break;
434                         }
435                         this_node = this_node->next;
436                 }
437         }
438
439         /* add "b" at this_node */
440         vp = this_node->next;
441         this_node->next = b;
442
443         /* run to end of "b" and connect the rest of "a" */
444         while (b->next)
445                 b = b->next;
446         b->next = vp;
447
448         return;
449 }
450
451 /*
452  * Function: rc_avpair_free
453  *
454  * Purpose: frees all value_pairs in the list
455  *
456  */
457
458 void rc_avpair_free (VALUE_PAIR *pair)
459 {
460         VALUE_PAIR     *next;
461
462         while (pair != (VALUE_PAIR *) NULL)
463         {
464                 next = pair->next;
465                 free (pair);
466                 pair = next;
467         }
468 }
469
470 /*
471  * Function: rc_fieldcpy
472  *
473  * Purpose: Copy a data field from the buffer.  Advance the buffer
474  *          past the data field.
475  *
476  */
477
478 static void rc_fieldcpy (char *string, char **uptr)
479 {
480         char           *ptr;
481
482         ptr = *uptr;
483         if (*ptr == '"')
484         {
485                 ptr++;
486                 while (*ptr != '"' && *ptr != '\0' && *ptr != '\n')
487                 {
488                         *string++ = *ptr++;
489                 }
490                 *string = '\0';
491                 if (*ptr == '"')
492                 {
493                         ptr++;
494                 }
495                 *uptr = ptr;
496                 return;
497         }
498
499         while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0' && *ptr != '\n' &&
500                         *ptr != '=' && *ptr != ',')
501         {
502                 *string++ = *ptr++;
503         }
504         *string = '\0';
505         *uptr = ptr;
506         return;
507 }
508
509
510 /*
511  * Function: rc_avpair_parse
512  *
513  * Purpose: parses the buffer to extract the attribute-value pairs.
514  *
515  * Returns: 0 = successful parse of attribute-value pair,
516  *         -1 = syntax (or other) error detected.
517  *
518  */
519
520 #define PARSE_MODE_NAME         0
521 #define PARSE_MODE_EQUAL        1
522 #define PARSE_MODE_VALUE        2
523 #define PARSE_MODE_INVALID      3
524
525 int rc_avpair_parse (char *buffer, VALUE_PAIR **first_pair)
526 {
527         int             mode;
528         char            attrstr[AUTH_ID_LEN];
529         char            valstr[AUTH_ID_LEN];
530         DICT_ATTR      *attr = NULL;
531         DICT_VALUE     *dval;
532         VALUE_PAIR     *pair;
533         VALUE_PAIR     *link;
534         struct tm      *tm;
535         time_t          timeval;
536
537         mode = PARSE_MODE_NAME;
538         while (*buffer != '\n' && *buffer != '\0')
539         {
540                 if (*buffer == ' ' || *buffer == '\t')
541                 {
542                         buffer++;
543                         continue;
544                 }
545
546                 switch (mode)
547                 {
548                     case PARSE_MODE_NAME:               /* Attribute Name */
549                         rc_fieldcpy (attrstr, &buffer);
550                         if ((attr =
551                                 rc_dict_findattr (attrstr)) == (DICT_ATTR *) NULL)
552                         {
553                                 rc_log(LOG_ERR, "rc_avpair_parse: unknown attribute");
554                                 if (*first_pair) {
555                                         rc_avpair_free(*first_pair);
556                                         *first_pair = (VALUE_PAIR *) NULL;
557                                 }
558                                 return (-1);
559                         }
560                         mode = PARSE_MODE_EQUAL;
561                         break;
562
563                     case PARSE_MODE_EQUAL:              /* Equal sign */
564                         if (*buffer == '=')
565                         {
566                                 mode = PARSE_MODE_VALUE;
567                                 buffer++;
568                         }
569                         else
570                         {
571                                 rc_log(LOG_ERR, "rc_avpair_parse: missing or misplaced equal sign");
572                                 if (*first_pair) {
573                                         rc_avpair_free(*first_pair);
574                                         *first_pair = (VALUE_PAIR *) NULL;
575                                 }
576                                 return (-1);
577                         }
578                         break;
579
580                     case PARSE_MODE_VALUE:              /* Value */
581                         rc_fieldcpy (valstr, &buffer);
582
583                         if ((pair =
584                                 (VALUE_PAIR *) malloc (sizeof (VALUE_PAIR)))
585                                                         == (VALUE_PAIR *) NULL)
586                         {
587                                 rc_log(LOG_CRIT, "rc_avpair_parse: out of memory");
588                                 if (*first_pair) {
589                                         rc_avpair_free(*first_pair);
590                                         *first_pair = (VALUE_PAIR *) NULL;
591                                 }
592                                 return (-1);
593                         }
594                         strcpy (pair->name, attr->name);
595                         pair->attribute = attr->value;
596                         pair->type = attr->type;
597                         pair->vendorcode = attr->vendorcode;
598
599                         switch (pair->type)
600                         {
601
602                             case PW_TYPE_STRING:
603                                 strcpy (pair->strvalue, valstr);
604                                 pair->lvalue = strlen(valstr);
605                                 break;
606
607                             case PW_TYPE_INTEGER:
608                                 if (isdigit (*valstr))
609                                 {
610                                         pair->lvalue = atoi (valstr);
611                                 }
612                                 else
613                                 {
614                                         if ((dval = rc_dict_findval (valstr))
615                                                         == (DICT_VALUE *) NULL)
616                                         {
617                                                 rc_log(LOG_ERR, "rc_avpair_parse: unknown attribute value: %s", valstr);
618                                                 if (*first_pair) {
619                                                         rc_avpair_free(*first_pair);
620                                                         *first_pair = (VALUE_PAIR *) NULL;
621                                                 }
622                                                 free (pair);
623                                                 return (-1);
624                                         }
625                                         else
626                                         {
627                                                 pair->lvalue = dval->value;
628                                         }
629                                 }
630                                 break;
631
632                             case PW_TYPE_IPADDR:
633                                 pair->lvalue = rc_get_ipaddr(valstr);
634                                 break;
635
636                             case PW_TYPE_DATE:
637                                 timeval = time (0);
638                                 tm = localtime (&timeval);
639                                 tm->tm_hour = 0;
640                                 tm->tm_min = 0;
641                                 tm->tm_sec = 0;
642                                 rc_str2tm (valstr, tm);
643 #ifdef TIMELOCAL
644                                 pair->lvalue = (UINT4) timelocal (tm);
645 #else   /* TIMELOCAL */
646                                 pair->lvalue = (UINT4) mktime (tm);
647 #endif  /* TIMELOCAL */
648                                 break;
649
650                             default:
651                                 rc_log(LOG_ERR, "rc_avpair_parse: unknown attribute type %d", pair->type);
652                                 if (*first_pair) {
653                                         rc_avpair_free(*first_pair);
654                                         *first_pair = (VALUE_PAIR *) NULL;
655                                 }
656                                 free (pair);
657                                 return (-1);
658                         }
659                         pair->next = (VALUE_PAIR *) NULL;
660
661                         if (*first_pair == (VALUE_PAIR *) NULL)
662                         {
663                                 *first_pair = pair;
664                         }
665                         else
666                         {
667                                 link = *first_pair;
668                                 while (link->next != (VALUE_PAIR *) NULL)
669                                 {
670                                         link = link->next;
671                                 }
672                                 link->next = pair;
673                         }
674
675                         mode = PARSE_MODE_NAME;
676                         break;
677
678                     default:
679                         mode = PARSE_MODE_NAME;
680                         break;
681                 }
682         }
683         return (0);
684 }
685
686 /*
687  * Function: rc_avpair_tostr
688  *
689  * Purpose: Translate an av_pair into two strings
690  *
691  * Returns: 0 on success, -1 on failure
692  *
693  */
694
695 int rc_avpair_tostr (VALUE_PAIR *pair, char *name, int ln, char *value, int lv)
696 {
697         DICT_VALUE     *dval;
698         char            buffer[32];
699         struct in_addr  inad;
700         unsigned char         *ptr;
701
702         *name = *value = '\0';
703
704         if (!pair || pair->name[0] == '\0') {
705                 rc_log(LOG_ERR, "rc_avpair_tostr: pair is NULL or empty");
706                 return (-1);
707         }
708
709         strncpy(name, pair->name, (size_t) ln);
710
711         switch (pair->type)
712         {
713             case PW_TYPE_STRING:
714                 lv--;
715                 ptr = (unsigned char *) pair->strvalue;
716                 while (*ptr != '\0')
717                 {
718                         if (!(isprint (*ptr)))
719                         {
720                                 sprintf (buffer, "\\%03o", *ptr);
721                                 strncat(value, buffer, (size_t) lv);
722                                 lv -= 4;
723                                 if (lv < 0) break;
724                         }
725                         else
726                         {
727                                 strncat(value, ptr, 1);
728                                 lv--;
729                                 if (lv < 0) break;
730                         }
731                         ptr++;
732                 }
733                 break;
734
735             case PW_TYPE_INTEGER:
736                 dval = rc_dict_getval (pair->lvalue, pair->name);
737                 if (dval != (DICT_VALUE *) NULL)
738                 {
739                         strncpy(value, dval->name, (size_t) lv-1);
740                 }
741                 else
742                 {
743                         sprintf (buffer, "%ld", pair->lvalue);
744                         strncpy(value, buffer, (size_t) lv);
745                 }
746                 break;
747
748             case PW_TYPE_IPADDR:
749                 inad.s_addr = htonl(pair->lvalue);
750                 strncpy (value, inet_ntoa (inad), (size_t) lv-1);
751                 break;
752
753             case PW_TYPE_DATE:
754                 strftime (buffer, sizeof (buffer), "%m/%d/%y %H:%M:%S",
755                           gmtime ((time_t *) & pair->lvalue));
756                 strncpy(value, buffer, lv-1);
757                 break;
758
759             default:
760                 rc_log(LOG_ERR, "rc_avpair_tostr: unknown attribute type %d", pair->type);
761                 return (-1);
762                 break;
763         }
764
765         return 0;
766 }
767
768 /*
769  * Function: rc_avpair_readin
770  *
771  * Purpose: get a sequence of attribute value pairs from the file input
772  *          and make them into a list of value_pairs
773  *
774  */
775
776 VALUE_PAIR *rc_avpair_readin(FILE *input)
777 {
778         VALUE_PAIR *vp = NULL;
779         char buffer[1024], *q;
780
781         while (fgets(buffer, sizeof(buffer), input) != NULL)
782         {
783                 q = buffer;
784
785                 while(*q && isspace(*q)) q++;
786
787                 if ((*q == '\n') || (*q == '#') || (*q == '\0'))
788                         continue;
789
790                 if (rc_avpair_parse(q, &vp) < 0) {
791                         rc_log(LOG_ERR, "rc_avpair_readin: malformed attribute: %s", buffer);
792                         rc_avpair_free(vp);
793                         return NULL;
794                 }
795         }
796
797         return vp;
798 }