1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or (at
5  *   your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: b1f8e0c2247e8b741bff3a62da93fad39b68e149 $
19  * @file serialize.c
20  * @brief Serialize and deserialise cache entries.
21  *
22  * @author Arran Cudbard-Bell
23  * @copyright 2014 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
24  * @copyright 2014 The FreeRADIUS server project
25  */
26 RCSID("$Id: b1f8e0c2247e8b741bff3a62da93fad39b68e149 $")
27 
28 #include "rlm_cache.h"
29 #include "serialize.h"
30 
31 /** Serialize a cache entry as a humanly readable string
32  *
33  * @param ctx to alloc new string in. Should be a talloc pool a little bigger
34  *	than the maximum serialized size of the entry.
35  * @param out Where to write pointer to serialized cache entry.
36  * @param c Cache entry to serialize.
37  * @return 0 on success else -1.
38  */
cache_serialize(TALLOC_CTX * ctx,char ** out,rlm_cache_entry_t * c)39 int cache_serialize(TALLOC_CTX *ctx, char **out, rlm_cache_entry_t *c)
40 {
41 	TALLOC_CTX *pairs = NULL;
42 
43 	vp_cursor_t cursor;
44 	VALUE_PAIR *vp;
45 
46 	char *to_store = NULL, *pair;
47 
48 	to_store = talloc_asprintf(ctx, "&Cache-Expires = %" PRIu64 "\n&Cache-Created = %" PRIu64 "\n",
49 				   (uint64_t)c->expires, (uint64_t)c->created);
50 	if (!to_store) goto error;
51 
52 	/*
53 	 *	It's valid to have an empty cache entry (save allocing the
54 	 *	pairs pool)
55 	 */
56 	if (!c->control && !c->packet && !c->reply) goto finish;
57 
58 	/*
59 	 *  In the majority of cases using these pools reduces the number of mallocs
60 	 *  to two, except in the case where the total serialized pairs length is
61 	 *  greater than the pairs pool, or the total serialized string is greater
62 	 *  than the store pool.
63 	 */
64 	pairs = talloc_pool(ctx, 512);
65 	if (!pairs) {
66 	error:
67 		talloc_free(pairs);
68 		return -1;
69 	}
70 
71 	if (c->control) {
72 		for (vp = fr_cursor_init(&cursor, &c->control);
73 		     vp;
74 		     vp = fr_cursor_next(&cursor)) {
75 			pair = vp_aprints(pairs, vp, '\'');
76 			if (!pair) goto error;
77 
78 			to_store = talloc_asprintf_append_buffer(to_store, "&control:%s\n", pair);
79 			if (!to_store) goto error;
80 		}
81 	}
82 
83 	if (c->packet) {
84 		for (vp = fr_cursor_init(&cursor, &c->packet);
85 		     vp;
86 		     vp = fr_cursor_next(&cursor)) {
87 			pair = vp_aprints(pairs, vp, '\'');
88 			if (!pair) goto error;
89 
90 			to_store = talloc_asprintf_append_buffer(to_store, "&%s\n", pair);
91 			if (!to_store) goto error;
92 		}
93 	}
94 
95 	if (c->reply) {
96 		for (vp = fr_cursor_init(&cursor, &c->reply);
97 		     vp;
98 		     vp = fr_cursor_next(&cursor)) {
99 			pair = vp_aprints(pairs, vp, '\'');
100 			if (!pair) goto error;
101 
102 			to_store = talloc_asprintf_append_buffer(to_store, "&reply:%s\n", pair);
103 			if (!to_store) goto error;
104 		}
105 	}
106 
107 	if (c->state) {
108 		for (vp = fr_cursor_init(&cursor, &c->state);
109 		     vp;
110 		     vp = fr_cursor_next(&cursor)) {
111 			pair = vp_aprints(pairs, vp, '\'');
112 			if (!pair) goto error;
113 
114 			to_store = talloc_asprintf_append_buffer(to_store, "&session-state:%s\n", pair);
115 			if (!to_store) goto error;
116 		}
117 	}
118 
119 finish:
120 	talloc_free(pairs);
121 	*out = to_store;
122 
123 	return 0;
124 }
125 
126 /** Converts a serialized cache entry back into a structure
127  *
128  * @param c Cache entry to populate (should already be allocated)
129  * @param in String representation of cache entry.
130  * @param inlen Length of string. May be < 0 in which case strlen will be
131  *	used to calculate the length of the string.
132  * @return 0 on success, -1 on error.
133  */
cache_deserialize(rlm_cache_entry_t * c,char * in,ssize_t inlen)134 int cache_deserialize(rlm_cache_entry_t *c, char *in, ssize_t inlen)
135 {
136 	vp_cursor_t packet, control, reply, state;
137 
138 	TALLOC_CTX *store = NULL;
139 	char *p, *q;
140 
141 	store = talloc_pool(c, 1024);
142 	if (!store) return -1;
143 
144 	if (inlen < 0) inlen = strlen(in);
145 
146 	fr_cursor_init(&packet, &c->packet);
147 	fr_cursor_init(&control, &c->control);
148 	fr_cursor_init(&reply, &c->reply);
149 	fr_cursor_init(&state, &c->state);
150 
151 	p = in;
152 
153 	while (((size_t)(p - in)) < (size_t)inlen) {
154 		vp_map_t *map = NULL;
155 		VALUE_PAIR *vp = NULL;
156 		ssize_t len;
157 
158 		q = strchr(p, '\n');
159 		if (!q) break;	/* List should also be terminated with a \n */
160 		*q = '\0';
161 
162 		if (map_afrom_attr_str(store, &map, p,
163 				       REQUEST_CURRENT, PAIR_LIST_REQUEST,
164 				       REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
165 			fr_strerror_printf("Failed parsing pair: %s", p);
166 			goto error;
167 		}
168 
169 		if (map->lhs->type != TMPL_TYPE_ATTR) {
170 			fr_strerror_printf("Pair left hand side \"%s\" parsed as %s, needed attribute.  "
171 					   "Check local dictionaries", map->lhs->name,
172 					   fr_int2str(tmpl_names, map->lhs->type, "<INVALID>"));
173 			goto error;
174 		}
175 
176 		if (map->rhs->type != TMPL_TYPE_LITERAL) {
177 			fr_strerror_printf("Pair right hand side \"%s\" parsed as %s, needed literal.  "
178 					   "Check serialized data quoting", map->rhs->name,
179 					   fr_int2str(tmpl_names, map->rhs->type, "<INVALID>"));
180 			goto error;
181 		}
182 
183 		/*
184 		 *	Convert literal to a type appropriate for the VP.
185 		 */
186 		if (tmpl_cast_in_place(map->rhs, map->lhs->tmpl_da->type, map->lhs->tmpl_da) < 0) goto error;
187 
188 		vp = fr_pair_afrom_da(c, map->lhs->tmpl_da);
189 		len = value_data_copy(vp, &vp->data, map->rhs->tmpl_data_type,
190 				      &map->rhs->tmpl_data_value, map->rhs->tmpl_data_length);
191 		if (len < 0) goto error;
192 		vp->vp_length = len;
193 
194 		/*
195 		 *	Pull out the special attributes, and set the
196 		 *	relevant cache entry fields.
197 		 */
198 		if (vp->da->vendor == 0) switch (vp->da->attr) {
199 		case PW_CACHE_CREATED:
200 			c->created = vp->vp_date;
201 			talloc_free(vp);
202 			goto next;
203 
204 		case PW_CACHE_EXPIRES:
205 			c->expires = vp->vp_date;
206 			talloc_free(vp);
207 			goto next;
208 
209 		default:
210 			break;
211 		}
212 
213 		switch (map->lhs->tmpl_list) {
214 		case PAIR_LIST_REQUEST:
215 			fr_cursor_insert(&packet, vp);
216 			break;
217 
218 		case PAIR_LIST_CONTROL:
219 			fr_cursor_insert(&control, vp);
220 			break;
221 
222 		case PAIR_LIST_REPLY:
223 			fr_cursor_insert(&reply, vp);
224 			break;
225 
226 		case PAIR_LIST_STATE:
227 			fr_cursor_insert(&state, vp);
228 			break;
229 
230 		default:
231 			fr_strerror_printf("Invalid cache list for pair: %s", p);
232 		error:
233 			talloc_free(vp);
234 			talloc_free(map);
235 			return -1;
236 		}
237 	next:
238 		p = q + 1;
239 		talloc_free(map);
240 	}
241 
242 	return 0;
243 }
244