handle zero size Base64 conversions
[strongswan.git] / src / medsrv / controller / peer_controller.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Copyright (C) 2008 Philip Boetschi, Adrian Doerig
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18 #include <string.h>
19
20 #include "peer_controller.h"
21
22 #include <library.h>
23 #include <debug.h>
24 #include <asn1/asn1.h>
25 #include <asn1/oid.h>
26 #include <utils/identification.h>
27 #include <credentials/keys/public_key.h>
28
29 typedef struct private_peer_controller_t private_peer_controller_t;
30
31 /**
32 * private data of the peer_controller
33 */
34 struct private_peer_controller_t {
35
36 /**
37 * public functions
38 */
39 peer_controller_t public;
40
41 /**
42 * active user session
43 */
44 user_t *user;
45
46 /**
47 * underlying database
48 */
49 database_t *db;
50 };
51
52 /**
53 * list the configured peer configs
54 */
55 static void list(private_peer_controller_t *this, request_t *request)
56 {
57 enumerator_t *query;
58
59 query = this->db->query(this->db,
60 "SELECT id, alias, keyid FROM peer WHERE user = ? ORDER BY alias",
61 DB_UINT, this->user->get_user(this->user),
62 DB_UINT, DB_TEXT, DB_BLOB);
63
64 if (query)
65 {
66 u_int id;
67 char *alias;
68 chunk_t keyid;
69 identification_t *identifier;
70
71 while (query->enumerate(query, &id, &alias, &keyid))
72 {
73 request->setf(request, "peers.%d.alias=%s", id, alias);
74 identifier = identification_create_from_encoding(ID_KEY_ID, keyid);
75 request->setf(request, "peers.%d.identifier=%Y", id, identifier);
76 identifier->destroy(identifier);
77 }
78 query->destroy(query);
79 }
80 request->render(request, "templates/peer/list.cs");
81 }
82
83 /**
84 * verify a peer alias
85 */
86 static bool verify_alias(private_peer_controller_t *this, request_t *request,
87 char *alias)
88 {
89 if (!alias || *alias == '\0')
90 {
91 request->setf(request, "error=Alias is missing.");
92 return FALSE;
93 }
94 while (*alias != '\0')
95 {
96 switch (*alias)
97 {
98 case 'a' ... 'z':
99 case 'A' ... 'Z':
100 case '0' ... '9':
101 case '-':
102 case '_':
103 case '@':
104 case '.':
105 alias++;
106 continue;
107 default:
108 request->setf(request, "error=Alias invalid, "
109 "valid characters: A-Z a-z 0-9 - _ @ .");
110 return FALSE;
111 }
112 }
113 return TRUE;
114 }
115
116 /**
117 * parse and verify a public key
118 */
119 static bool parse_public_key(private_peer_controller_t *this,
120 request_t *request, char *public_key,
121 chunk_t *encoding, chunk_t *keyid)
122 {
123 public_key_t *public;
124 chunk_t blob, id;
125
126 if (!public_key || *public_key == '\0')
127 {
128 request->setf(request, "error=Public key is missing.");
129 return FALSE;
130 }
131 blob = chunk_clone(chunk_create(public_key, strlen(public_key)));
132 public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
133 BUILD_BLOB_PEM, blob,
134 BUILD_END);
135 chunk_free(&blob);
136 if (!public)
137 {
138 request->setf(request, "error=Parsing public key failed.");
139 return FALSE;
140 }
141 /* TODO: use get_encoding() with an encoding type */
142 if (!public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &id) ||
143 !public->get_encoding(public, PUBKEY_SPKI_ASN1_DER, encoding))
144 {
145 request->setf(request, "error=Encoding public key failed.");
146 return FALSE;
147 }
148 *keyid = chunk_clone(id);
149 public->destroy(public);
150 return TRUE;
151 }
152
153 /**
154 * register a new peer
155 */
156 static void add(private_peer_controller_t *this, request_t *request)
157 {
158 char *alias = "", *public_key = "";
159
160 if (request->get_query_data(request, "back"))
161 {
162 return request->redirect(request, "peer/list");
163 }
164 while (request->get_query_data(request, "add"))
165 {
166 chunk_t encoding, keyid;
167
168 alias = request->get_query_data(request, "alias");
169 public_key = request->get_query_data(request, "public_key");
170
171 if (!verify_alias(this, request, alias))
172 {
173 break;
174 }
175 if (!parse_public_key(this, request, public_key, &encoding, &keyid))
176 {
177 break;
178 }
179 if (this->db->execute(this->db, NULL,
180 "INSERT INTO peer (user, alias, public_key, keyid) "
181 "VALUES (?, ?, ?, ?)",
182 DB_UINT, this->user->get_user(this->user),
183 DB_TEXT, alias, DB_BLOB, encoding,
184 DB_BLOB, keyid) <= 0)
185 {
186 request->setf(request, "error=Peer already exists.");
187 free(keyid.ptr);
188 free(encoding.ptr);
189 break;
190 }
191 free(keyid.ptr);
192 free(encoding.ptr);
193 return request->redirect(request, "peer/list");
194 }
195 request->set(request, "alias", alias);
196 request->set(request, "public_key", public_key);
197
198 return request->render(request, "templates/peer/add.cs");
199 }
200
201 /**
202 * pem encode a public key into an allocated string
203 */
204 char* pem_encode(chunk_t der)
205 {
206 static const char *begin = "-----BEGIN PUBLIC KEY-----\n";
207 static const char *end = "-----END PUBLIC KEY-----";
208 size_t len;
209 char *pem;
210 chunk_t base64;
211 int i = 0;
212
213 base64 = chunk_to_base64(der, NULL);
214 len = strlen(begin) + base64.len + base64.len/64 + strlen(end) + 2;
215 pem = malloc(len + 1);
216
217 strcpy(pem, begin);
218 do
219 {
220 strncat(pem, base64.ptr + i, 64);
221 strcat(pem, "\n");
222 i += 64;
223 }
224 while (i < base64.len - 2);
225 strcat(pem, end);
226
227 free(base64.ptr);
228 return pem;
229 }
230
231 /**
232 * edit a peer
233 */
234 static void edit(private_peer_controller_t *this, request_t *request, int id)
235 {
236 char *alias = "", *public_key = "", *pem;
237 chunk_t encoding, keyid;
238
239 if (request->get_query_data(request, "back"))
240 {
241 return request->redirect(request, "peer/list");
242 }
243 if (request->get_query_data(request, "delete"))
244 {
245 this->db->execute(this->db, NULL,
246 "DELETE FROM peer WHERE id = ? AND user = ?",
247 DB_INT, id, DB_UINT, this->user->get_user(this->user));
248 return request->redirect(request, "peer/list");
249 }
250 if (request->get_query_data(request, "save"))
251 {
252 while (TRUE)
253 {
254 alias = request->get_query_data(request, "alias");
255 public_key = request->get_query_data(request, "public_key");
256
257 if (!verify_alias(this, request, alias))
258 {
259 break;
260 }
261 if (!parse_public_key(this, request, public_key, &encoding, &keyid))
262 {
263 break;
264 }
265 if (this->db->execute(this->db, NULL,
266 "UPDATE peer SET alias = ?, public_key = ?, keyid = ? "
267 "WHERE id = ? AND user = ?",
268 DB_TEXT, alias, DB_BLOB, encoding, DB_BLOB, keyid,
269 DB_INT, id, DB_UINT, this->user->get_user(this->user)) < 0)
270 {
271 request->setf(request, "error=Peer already exists.");
272 free(keyid.ptr);
273 free(encoding.ptr);
274 break;
275 }
276 free(keyid.ptr);
277 free(encoding.ptr);
278 return request->redirect(request, "peer/list");
279 }
280 }
281 else
282 {
283 enumerator_t *query = this->db->query(this->db,
284 "SELECT alias, public_key FROM peer WHERE id = ? AND user = ?",
285 DB_INT, id, DB_UINT, this->user->get_user(this->user),
286 DB_TEXT, DB_BLOB);
287 if (query && query->enumerate(query, &alias, &encoding))
288 {
289 alias = strdupa(alias);
290 pem = pem_encode(encoding);
291 public_key = strdupa(pem);
292 free(pem);
293 }
294 else
295 {
296 return request->redirect(request, "peer/list");
297 }
298 DESTROY_IF(query);
299 }
300 request->set(request, "alias", alias);
301 request->set(request, "public_key", public_key);
302 return request->render(request, "templates/peer/edit.cs");
303 }
304
305 /**
306 * delete a peer from the database
307 */
308 static void delete(private_peer_controller_t *this, request_t *request, int id)
309 {
310 this->db->execute(this->db, NULL,
311 "DELETE FROM peer WHERE id = ? AND user = ?",
312 DB_INT, id, DB_UINT, this->user->get_user(this->user));
313 }
314
315 /**
316 * Implementation of controller_t.get_name
317 */
318 static char* get_name(private_peer_controller_t *this)
319 {
320 return "peer";
321 }
322
323 /**
324 * Implementation of controller_t.handle
325 */
326 static void handle(private_peer_controller_t *this, request_t *request,
327 char *action, char *idstr)
328 {
329 if (action)
330 {
331 int id = 0;
332 if (idstr)
333 {
334 id = atoi(idstr);
335 }
336
337 if (streq(action, "list"))
338 {
339 return list(this, request);
340 }
341 else if (streq(action, "add"))
342 {
343 return add(this, request);
344 }
345 else if (streq(action, "edit") && id)
346 {
347 return edit(this, request, id);
348 }
349 else if (streq(action, "delete") && id)
350 {
351 delete(this, request, id);
352 }
353 }
354 request->redirect(request, "peer/list");
355 }
356
357 /**
358 * Implementation of controller_t.destroy
359 */
360 static void destroy(private_peer_controller_t *this)
361 {
362 free(this);
363 }
364
365 /*
366 * see header file
367 */
368 controller_t *peer_controller_create(user_t *user, database_t *db)
369 {
370 private_peer_controller_t *this= malloc_thing(private_peer_controller_t);
371
372 this->public.controller.get_name = (char*(*)(controller_t*))get_name;
373 this->public.controller.handle = (void(*)(controller_t*, request_t*, char*, char*, char*, char*, char*))handle;
374 this->public.controller.destroy = (void(*)(controller_t*))destroy;
375
376 this->user = user;
377 this->db = db;
378
379 return &this->public.controller;
380 }
381