diff options
Diffstat (limited to 'src/medsrv/controller/peer_controller.c')
-rwxr-xr-x | src/medsrv/controller/peer_controller.c | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/src/medsrv/controller/peer_controller.c b/src/medsrv/controller/peer_controller.c new file mode 100755 index 000000000..22fc6df2f --- /dev/null +++ b/src/medsrv/controller/peer_controller.c @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2008 Martin Willi + * Copyright (C) 2008 Philip Boetschi, Adrian Doerig + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * $Id$ + */ + +#define _GNU_SOURCE +#include <string.h> + +#include "peer_controller.h" + +#include <library.h> +#include <debug.h> +#include <asn1/asn1.h> +#include <asn1/oid.h> + +typedef struct private_peer_controller_t private_peer_controller_t; + +/** + * private data of the peer_controller + */ +struct private_peer_controller_t { + + /** + * public functions + */ + peer_controller_t public; + + /** + * active user session + */ + user_t *user; + + /** + * underlying database + */ + database_t *db; +}; + +/** + * list the configured peer configs + */ +static void list(private_peer_controller_t *this, request_t *request) +{ + enumerator_t *query; + + query = this->db->query(this->db, + "SELECT id, alias, keyid FROM peer WHERE user = ? ORDER BY alias", + DB_UINT, this->user->get_user(this->user), + DB_UINT, DB_TEXT, DB_BLOB); + + if (query) + { + u_int id; + char *alias; + chunk_t keyid; + identification_t *identifier; + + while (query->enumerate(query, &id, &alias, &keyid)) + { + request->setf(request, "peers.%d.alias=%s", id, alias); + identifier = identification_create_from_encoding(ID_KEY_ID, keyid); + request->setf(request, "peers.%d.identifier=%D", id, identifier); + identifier->destroy(identifier); + } + query->destroy(query); + } + request->render(request, "templates/peer/list.cs"); +} + +/** + * verify a peer alias + */ +static bool verify_alias(private_peer_controller_t *this, request_t *request, + char *alias) +{ + if (!alias || *alias == '\0') + { + request->setf(request, "error=Alias is missing."); + return FALSE; + } + while (*alias != '\0') + { + switch (*alias) + { + case 'a' ... 'z': + case 'A' ... 'Z': + case '0' ... '9': + case '-': + case '_': + case '@': + case '.': + alias++; + continue; + default: + request->setf(request, "error=Alias invalid, " + "valid characters: A-Z a-z 0-9 - _ @ ."); + return FALSE; + } + } + return TRUE; +} + +/** + * parse and verify a public key + */ +static bool parse_public_key(private_peer_controller_t *this, + request_t *request, char *public_key, + chunk_t *encoding, chunk_t *keyid) +{ + public_key_t *public; + identification_t *id; + + if (!public_key || *public_key == '\0') + { + request->setf(request, "error=Public key is missing."); + return FALSE; + } + public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY, + BUILD_BLOB_ASN1_PEM, public_key, + BUILD_END); + if (!public) + { + request->setf(request, "error=Parsing public key failed."); + return FALSE; + } + /* TODO: use get_encoding() with an encoding type */ + *encoding = asn1_wrap(ASN1_SEQUENCE, "cm", + asn1_algorithmIdentifier(OID_RSA_ENCRYPTION), + asn1_bitstring("m", public->get_encoding(public))); + id = public->get_id(public, ID_PUBKEY_SHA1); + *keyid = chunk_clone(id->get_encoding(id)); + public->destroy(public); + return TRUE; +} + +/** + * register a new peer + */ +static void add(private_peer_controller_t *this, request_t *request) +{ + char *alias = "", *public_key = ""; + + if (request->get_query_data(request, "back")) + { + return request->redirect(request, "peer/list"); + } + while (request->get_query_data(request, "add")) + { + chunk_t encoding, keyid; + + alias = request->get_query_data(request, "alias"); + public_key = request->get_query_data(request, "public_key"); + + if (!verify_alias(this, request, alias)) + { + break; + } + if (!parse_public_key(this, request, public_key, &encoding, &keyid)) + { + break; + } + if (this->db->execute(this->db, NULL, + "INSERT INTO peer (user, alias, public_key, keyid) " + "VALUES (?, ?, ?, ?)", + DB_UINT, this->user->get_user(this->user), + DB_TEXT, alias, DB_BLOB, encoding, + DB_BLOB, keyid) <= 0) + { + request->setf(request, "error=Peer already exists."); + free(keyid.ptr); + free(encoding.ptr); + break; + } + free(keyid.ptr); + free(encoding.ptr); + return request->redirect(request, "peer/list"); + } + request->set(request, "alias", alias); + request->set(request, "public_key", public_key); + + return request->render(request, "templates/peer/add.cs"); +} + +/** + * pem encode a public key into an allocated string + */ +char* pem_encode(chunk_t der) +{ + static const char *begin = "-----BEGIN PUBLIC KEY-----\n"; + static const char *end = "-----END PUBLIC KEY-----"; + size_t len; + char *pem; + chunk_t base64; + int i = 0; + + base64 = chunk_to_base64(der, NULL); + len = strlen(begin) + base64.len + base64.len/64 + strlen(end) + 2; + pem = malloc(len + 1); + + strcpy(pem, begin); + do + { + strncat(pem, base64.ptr + i, 64); + strcat(pem, "\n"); + i += 64; + } + while (i < base64.len - 2); + strcat(pem, end); + + free(base64.ptr); + return pem; +} + +/** + * edit a peer + */ +static void edit(private_peer_controller_t *this, request_t *request, int id) +{ + char *alias = "", *public_key = "", *pem; + chunk_t encoding, keyid; + + if (request->get_query_data(request, "back")) + { + return request->redirect(request, "peer/list"); + } + if (request->get_query_data(request, "delete")) + { + this->db->execute(this->db, NULL, + "DELETE FROM peer WHERE id = ? AND user = ?", + DB_INT, id, DB_UINT, this->user->get_user(this->user)); + return request->redirect(request, "peer/list"); + } + if (request->get_query_data(request, "save")) + { + while (TRUE) + { + alias = request->get_query_data(request, "alias"); + public_key = request->get_query_data(request, "public_key"); + + if (!verify_alias(this, request, alias)) + { + break; + } + if (!parse_public_key(this, request, public_key, &encoding, &keyid)) + { + break; + } + if (this->db->execute(this->db, NULL, + "UPDATE peer SET alias = ?, public_key = ?, keyid = ? " + "WHERE id = ? AND user = ?", + DB_TEXT, alias, DB_BLOB, encoding, DB_BLOB, keyid, + DB_INT, id, DB_UINT, this->user->get_user(this->user)) < 0) + { + request->setf(request, "error=Peer already exists."); + free(keyid.ptr); + free(encoding.ptr); + break; + } + free(keyid.ptr); + free(encoding.ptr); + return request->redirect(request, "peer/list"); + } + } + else + { + enumerator_t *query = this->db->query(this->db, + "SELECT alias, public_key FROM peer WHERE id = ? AND user = ?", + DB_INT, id, DB_UINT, this->user->get_user(this->user), + DB_TEXT, DB_BLOB); + if (query && query->enumerate(query, &alias, &encoding)) + { + alias = strdupa(alias); + pem = pem_encode(encoding); + public_key = strdupa(pem); + free(pem); + } + else + { + return request->redirect(request, "peer/list"); + } + DESTROY_IF(query); + } + request->set(request, "alias", alias); + request->set(request, "public_key", public_key); + return request->render(request, "templates/peer/edit.cs"); +} + +/** + * delete a peer from the database + */ +static void delete(private_peer_controller_t *this, request_t *request, int id) +{ + this->db->execute(this->db, NULL, + "DELETE FROM peer WHERE id = ? AND user = ?", + DB_INT, id, DB_UINT, this->user->get_user(this->user)); +} + +/** + * Implementation of controller_t.get_name + */ +static char* get_name(private_peer_controller_t *this) +{ + return "peer"; +} + +/** + * Implementation of controller_t.handle + */ +static void handle(private_peer_controller_t *this, request_t *request, + char *action, char *idstr) +{ + if (action) + { + int id = 0; + if (idstr) + { + id = atoi(idstr); + } + + if (streq(action, "list")) + { + return list(this, request); + } + else if (streq(action, "add")) + { + return add(this, request); + } + else if (streq(action, "edit") && id) + { + return edit(this, request, id); + } + else if (streq(action, "delete") && id) + { + delete(this, request, id); + } + } + request->redirect(request, "peer/list"); +} + +/** + * Implementation of controller_t.destroy + */ +static void destroy(private_peer_controller_t *this) +{ + free(this); +} + +/* + * see header file + */ +controller_t *peer_controller_create(user_t *user, database_t *db) +{ + private_peer_controller_t *this= malloc_thing(private_peer_controller_t); + + this->public.controller.get_name = (char*(*)(controller_t*))get_name; + this->public.controller.handle = (void(*)(controller_t*, request_t*, char*, char*, char*, char*, char*))handle; + this->public.controller.destroy = (void(*)(controller_t*))destroy; + + this->user = user; + this->db = db; + + return &this->public.controller; +} + |