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