added mediation server web frontend
[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 * $Id$
17 */
18
19 #define _GNU_SOURCE
20 #include <string.h>
21
22 #include "peer_controller.h"
23
24 #include <library.h>
25 #include <debug.h>
26 #include <asn1/asn1.h>
27 #include <asn1/oid.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=%D", 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 identification_t *id;
125
126 if (!public_key || *public_key == '\0')
127 {
128 request->setf(request, "error=Public key is missing.");
129 return FALSE;
130 }
131 public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
132 BUILD_BLOB_ASN1_PEM, public_key,
133 BUILD_END);
134 if (!public)
135 {
136 request->setf(request, "error=Parsing public key failed.");
137 return FALSE;
138 }
139 /* TODO: use get_encoding() with an encoding type */
140 *encoding = asn1_wrap(ASN1_SEQUENCE, "cm",
141 asn1_algorithmIdentifier(OID_RSA_ENCRYPTION),
142 asn1_bitstring("m", public->get_encoding(public)));
143 id = public->get_id(public, ID_PUBKEY_SHA1);
144 *keyid = chunk_clone(id->get_encoding(id));
145 public->destroy(public);
146 return TRUE;
147 }
148
149 /**
150 * register a new peer
151 */
152 static void add(private_peer_controller_t *this, request_t *request)
153 {
154 char *alias = "", *public_key = "";
155
156 if (request->get_query_data(request, "back"))
157 {
158 return request->redirect(request, "peer/list");
159 }
160 while (request->get_query_data(request, "add"))
161 {
162 chunk_t encoding, keyid;
163
164 alias = request->get_query_data(request, "alias");
165 public_key = request->get_query_data(request, "public_key");
166
167 if (!verify_alias(this, request, alias))
168 {
169 break;
170 }
171 if (!parse_public_key(this, request, public_key, &encoding, &keyid))
172 {
173 break;
174 }
175 if (this->db->execute(this->db, NULL,
176 "INSERT INTO peer (user, alias, public_key, keyid) "
177 "VALUES (?, ?, ?, ?)",
178 DB_UINT, this->user->get_user(this->user),
179 DB_TEXT, alias, DB_BLOB, encoding,
180 DB_BLOB, keyid) <= 0)
181 {
182 request->setf(request, "error=Peer already exists.");
183 free(keyid.ptr);
184 free(encoding.ptr);
185 break;
186 }
187 free(keyid.ptr);
188 free(encoding.ptr);
189 return request->redirect(request, "peer/list");
190 }
191 request->set(request, "alias", alias);
192 request->set(request, "public_key", public_key);
193
194 return request->render(request, "templates/peer/add.cs");
195 }
196
197 /**
198 * pem encode a public key into an allocated string
199 */
200 char* pem_encode(chunk_t der)
201 {
202 static const char *begin = "-----BEGIN PUBLIC KEY-----\n";
203 static const char *end = "-----END PUBLIC KEY-----";
204 size_t len;
205 char *pem;
206 chunk_t base64;
207 int i = 0;
208
209 base64 = chunk_to_base64(der, NULL);
210 len = strlen(begin) + base64.len + base64.len/64 + strlen(end) + 2;
211 pem = malloc(len + 1);
212
213 strcpy(pem, begin);
214 do
215 {
216 strncat(pem, base64.ptr + i, 64);
217 strcat(pem, "\n");
218 i += 64;
219 }
220 while (i < base64.len - 2);
221 strcat(pem, end);
222
223 free(base64.ptr);
224 return pem;
225 }
226
227 /**
228 * edit a peer
229 */
230 static void edit(private_peer_controller_t *this, request_t *request, int id)
231 {
232 char *alias = "", *public_key = "", *pem;
233 chunk_t encoding, keyid;
234
235 if (request->get_query_data(request, "back"))
236 {
237 return request->redirect(request, "peer/list");
238 }
239 if (request->get_query_data(request, "delete"))
240 {
241 this->db->execute(this->db, NULL,
242 "DELETE FROM peer WHERE id = ? AND user = ?",
243 DB_INT, id, DB_UINT, this->user->get_user(this->user));
244 return request->redirect(request, "peer/list");
245 }
246 if (request->get_query_data(request, "save"))
247 {
248 while (TRUE)
249 {
250 alias = request->get_query_data(request, "alias");
251 public_key = request->get_query_data(request, "public_key");
252
253 if (!verify_alias(this, request, alias))
254 {
255 break;
256 }
257 if (!parse_public_key(this, request, public_key, &encoding, &keyid))
258 {
259 break;
260 }
261 if (this->db->execute(this->db, NULL,
262 "UPDATE peer SET alias = ?, public_key = ?, keyid = ? "
263 "WHERE id = ? AND user = ?",
264 DB_TEXT, alias, DB_BLOB, encoding, DB_BLOB, keyid,
265 DB_INT, id, DB_UINT, this->user->get_user(this->user)) < 0)
266 {
267 request->setf(request, "error=Peer already exists.");
268 free(keyid.ptr);
269 free(encoding.ptr);
270 break;
271 }
272 free(keyid.ptr);
273 free(encoding.ptr);
274 return request->redirect(request, "peer/list");
275 }
276 }
277 else
278 {
279 enumerator_t *query = this->db->query(this->db,
280 "SELECT alias, public_key FROM peer WHERE id = ? AND user = ?",
281 DB_INT, id, DB_UINT, this->user->get_user(this->user),
282 DB_TEXT, DB_BLOB);
283 if (query && query->enumerate(query, &alias, &encoding))
284 {
285 alias = strdupa(alias);
286 pem = pem_encode(encoding);
287 public_key = strdupa(pem);
288 free(pem);
289 }
290 else
291 {
292 return request->redirect(request, "peer/list");
293 }
294 DESTROY_IF(query);
295 }
296 request->set(request, "alias", alias);
297 request->set(request, "public_key", public_key);
298 return request->render(request, "templates/peer/edit.cs");
299 }
300
301 /**
302 * delete a peer from the database
303 */
304 static void delete(private_peer_controller_t *this, request_t *request, int id)
305 {
306 this->db->execute(this->db, NULL,
307 "DELETE FROM peer WHERE id = ? AND user = ?",
308 DB_INT, id, DB_UINT, this->user->get_user(this->user));
309 }
310
311 /**
312 * Implementation of controller_t.get_name
313 */
314 static char* get_name(private_peer_controller_t *this)
315 {
316 return "peer";
317 }
318
319 /**
320 * Implementation of controller_t.handle
321 */
322 static void handle(private_peer_controller_t *this, request_t *request,
323 char *action, char *idstr)
324 {
325 if (action)
326 {
327 int id = 0;
328 if (idstr)
329 {
330 id = atoi(idstr);
331 }
332
333 if (streq(action, "list"))
334 {
335 return list(this, request);
336 }
337 else if (streq(action, "add"))
338 {
339 return add(this, request);
340 }
341 else if (streq(action, "edit") && id)
342 {
343 return edit(this, request, id);
344 }
345 else if (streq(action, "delete") && id)
346 {
347 delete(this, request, id);
348 }
349 }
350 request->redirect(request, "peer/list");
351 }
352
353 /**
354 * Implementation of controller_t.destroy
355 */
356 static void destroy(private_peer_controller_t *this)
357 {
358 free(this);
359 }
360
361 /*
362 * see header file
363 */
364 controller_t *peer_controller_create(user_t *user, database_t *db)
365 {
366 private_peer_controller_t *this= malloc_thing(private_peer_controller_t);
367
368 this->public.controller.get_name = (char*(*)(controller_t*))get_name;
369 this->public.controller.handle = (void(*)(controller_t*, request_t*, char*, char*, char*, char*, char*))handle;
370 this->public.controller.destroy = (void(*)(controller_t*))destroy;
371
372 this->user = user;
373 this->db = db;
374
375 return &this->public.controller;
376 }
377