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