added 2 3DES-CBC and 3 AES-CBC test vectors
[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
27 typedef struct private_peer_controller_t private_peer_controller_t;
28
29 /**
30 * private data of the peer_controller
31 */
32 struct private_peer_controller_t {
33
34 /**
35 * public functions
36 */
37 peer_controller_t public;
38
39 /**
40 * active user session
41 */
42 user_t *user;
43
44 /**
45 * underlying database
46 */
47 database_t *db;
48 };
49
50 /**
51 * list the configured peer configs
52 */
53 static void list(private_peer_controller_t *this, request_t *request)
54 {
55 enumerator_t *query;
56
57 query = this->db->query(this->db,
58 "SELECT id, alias, keyid FROM peer WHERE user = ? ORDER BY alias",
59 DB_UINT, this->user->get_user(this->user),
60 DB_UINT, DB_TEXT, DB_BLOB);
61
62 if (query)
63 {
64 u_int id;
65 char *alias;
66 chunk_t keyid;
67 identification_t *identifier;
68
69 while (query->enumerate(query, &id, &alias, &keyid))
70 {
71 request->setf(request, "peers.%d.alias=%s", id, alias);
72 identifier = identification_create_from_encoding(ID_KEY_ID, keyid);
73 request->setf(request, "peers.%d.identifier=%Y", id, identifier);
74 identifier->destroy(identifier);
75 }
76 query->destroy(query);
77 }
78 request->render(request, "templates/peer/list.cs");
79 }
80
81 /**
82 * verify a peer alias
83 */
84 static bool verify_alias(private_peer_controller_t *this, request_t *request,
85 char *alias)
86 {
87 if (!alias || *alias == '\0')
88 {
89 request->setf(request, "error=Alias is missing.");
90 return FALSE;
91 }
92 while (*alias != '\0')
93 {
94 switch (*alias)
95 {
96 case 'a' ... 'z':
97 case 'A' ... 'Z':
98 case '0' ... '9':
99 case '-':
100 case '_':
101 case '@':
102 case '.':
103 alias++;
104 continue;
105 default:
106 request->setf(request, "error=Alias invalid, "
107 "valid characters: A-Z a-z 0-9 - _ @ .");
108 return FALSE;
109 }
110 }
111 return TRUE;
112 }
113
114 /**
115 * parse and verify a public key
116 */
117 static bool parse_public_key(private_peer_controller_t *this,
118 request_t *request, char *public_key,
119 chunk_t *encoding, chunk_t *keyid)
120 {
121 public_key_t *public;
122 identification_t *id;
123
124 if (!public_key || *public_key == '\0')
125 {
126 request->setf(request, "error=Public key is missing.");
127 return FALSE;
128 }
129 public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
130 BUILD_BLOB_ASN1_PEM, public_key,
131 BUILD_END);
132 if (!public)
133 {
134 request->setf(request, "error=Parsing public key failed.");
135 return FALSE;
136 }
137 /* TODO: use get_encoding() with an encoding type */
138 *encoding = asn1_wrap(ASN1_SEQUENCE, "cm",
139 asn1_algorithmIdentifier(OID_RSA_ENCRYPTION),
140 asn1_bitstring("m", public->get_encoding(public)));
141 id = public->get_id(public, ID_PUBKEY_SHA1);
142 *keyid = chunk_clone(id->get_encoding(id));
143 public->destroy(public);
144 return TRUE;
145 }
146
147 /**
148 * register a new peer
149 */
150 static void add(private_peer_controller_t *this, request_t *request)
151 {
152 char *alias = "", *public_key = "";
153
154 if (request->get_query_data(request, "back"))
155 {
156 return request->redirect(request, "peer/list");
157 }
158 while (request->get_query_data(request, "add"))
159 {
160 chunk_t encoding, keyid;
161
162 alias = request->get_query_data(request, "alias");
163 public_key = request->get_query_data(request, "public_key");
164
165 if (!verify_alias(this, request, alias))
166 {
167 break;
168 }
169 if (!parse_public_key(this, request, public_key, &encoding, &keyid))
170 {
171 break;
172 }
173 if (this->db->execute(this->db, NULL,
174 "INSERT INTO peer (user, alias, public_key, keyid) "
175 "VALUES (?, ?, ?, ?)",
176 DB_UINT, this->user->get_user(this->user),
177 DB_TEXT, alias, DB_BLOB, encoding,
178 DB_BLOB, keyid) <= 0)
179 {
180 request->setf(request, "error=Peer already exists.");
181 free(keyid.ptr);
182 free(encoding.ptr);
183 break;
184 }
185 free(keyid.ptr);
186 free(encoding.ptr);
187 return request->redirect(request, "peer/list");
188 }
189 request->set(request, "alias", alias);
190 request->set(request, "public_key", public_key);
191
192 return request->render(request, "templates/peer/add.cs");
193 }
194
195 /**
196 * pem encode a public key into an allocated string
197 */
198 char* pem_encode(chunk_t der)
199 {
200 static const char *begin = "-----BEGIN PUBLIC KEY-----\n";
201 static const char *end = "-----END PUBLIC KEY-----";
202 size_t len;
203 char *pem;
204 chunk_t base64;
205 int i = 0;
206
207 base64 = chunk_to_base64(der, NULL);
208 len = strlen(begin) + base64.len + base64.len/64 + strlen(end) + 2;
209 pem = malloc(len + 1);
210
211 strcpy(pem, begin);
212 do
213 {
214 strncat(pem, base64.ptr + i, 64);
215 strcat(pem, "\n");
216 i += 64;
217 }
218 while (i < base64.len - 2);
219 strcat(pem, end);
220
221 free(base64.ptr);
222 return pem;
223 }
224
225 /**
226 * edit a peer
227 */
228 static void edit(private_peer_controller_t *this, request_t *request, int id)
229 {
230 char *alias = "", *public_key = "", *pem;
231 chunk_t encoding, keyid;
232
233 if (request->get_query_data(request, "back"))
234 {
235 return request->redirect(request, "peer/list");
236 }
237 if (request->get_query_data(request, "delete"))
238 {
239 this->db->execute(this->db, NULL,
240 "DELETE FROM peer WHERE id = ? AND user = ?",
241 DB_INT, id, DB_UINT, this->user->get_user(this->user));
242 return request->redirect(request, "peer/list");
243 }
244 if (request->get_query_data(request, "save"))
245 {
246 while (TRUE)
247 {
248 alias = request->get_query_data(request, "alias");
249 public_key = request->get_query_data(request, "public_key");
250
251 if (!verify_alias(this, request, alias))
252 {
253 break;
254 }
255 if (!parse_public_key(this, request, public_key, &encoding, &keyid))
256 {
257 break;
258 }
259 if (this->db->execute(this->db, NULL,
260 "UPDATE peer SET alias = ?, public_key = ?, keyid = ? "
261 "WHERE id = ? AND user = ?",
262 DB_TEXT, alias, DB_BLOB, encoding, DB_BLOB, keyid,
263 DB_INT, id, DB_UINT, this->user->get_user(this->user)) < 0)
264 {
265 request->setf(request, "error=Peer already exists.");
266 free(keyid.ptr);
267 free(encoding.ptr);
268 break;
269 }
270 free(keyid.ptr);
271 free(encoding.ptr);
272 return request->redirect(request, "peer/list");
273 }
274 }
275 else
276 {
277 enumerator_t *query = this->db->query(this->db,
278 "SELECT alias, public_key FROM peer WHERE id = ? AND user = ?",
279 DB_INT, id, DB_UINT, this->user->get_user(this->user),
280 DB_TEXT, DB_BLOB);
281 if (query && query->enumerate(query, &alias, &encoding))
282 {
283 alias = strdupa(alias);
284 pem = pem_encode(encoding);
285 public_key = strdupa(pem);
286 free(pem);
287 }
288 else
289 {
290 return request->redirect(request, "peer/list");
291 }
292 DESTROY_IF(query);
293 }
294 request->set(request, "alias", alias);
295 request->set(request, "public_key", public_key);
296 return request->render(request, "templates/peer/edit.cs");
297 }
298
299 /**
300 * delete a peer from the database
301 */
302 static void delete(private_peer_controller_t *this, request_t *request, int id)
303 {
304 this->db->execute(this->db, NULL,
305 "DELETE FROM peer WHERE id = ? AND user = ?",
306 DB_INT, id, DB_UINT, this->user->get_user(this->user));
307 }
308
309 /**
310 * Implementation of controller_t.get_name
311 */
312 static char* get_name(private_peer_controller_t *this)
313 {
314 return "peer";
315 }
316
317 /**
318 * Implementation of controller_t.handle
319 */
320 static void handle(private_peer_controller_t *this, request_t *request,
321 char *action, char *idstr)
322 {
323 if (action)
324 {
325 int id = 0;
326 if (idstr)
327 {
328 id = atoi(idstr);
329 }
330
331 if (streq(action, "list"))
332 {
333 return list(this, request);
334 }
335 else if (streq(action, "add"))
336 {
337 return add(this, request);
338 }
339 else if (streq(action, "edit") && id)
340 {
341 return edit(this, request, id);
342 }
343 else if (streq(action, "delete") && id)
344 {
345 delete(this, request, id);
346 }
347 }
348 request->redirect(request, "peer/list");
349 }
350
351 /**
352 * Implementation of controller_t.destroy
353 */
354 static void destroy(private_peer_controller_t *this)
355 {
356 free(this);
357 }
358
359 /*
360 * see header file
361 */
362 controller_t *peer_controller_create(user_t *user, database_t *db)
363 {
364 private_peer_controller_t *this= malloc_thing(private_peer_controller_t);
365
366 this->public.controller.get_name = (char*(*)(controller_t*))get_name;
367 this->public.controller.handle = (void(*)(controller_t*, request_t*, char*, char*, char*, char*, char*))handle;
368 this->public.controller.destroy = (void(*)(controller_t*))destroy;
369
370 this->user = user;
371 this->db = db;
372
373 return &this->public.controller;
374 }
375