380 lines
8.6 KiB
C
380 lines
8.6 KiB
C
/*
|
|
* Copyright (C) 2008 Martin Willi
|
|
* Copyright (C) 2008 Philip Boetschi, Adrian Doerig
|
|
* HSR 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.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <string.h>
|
|
|
|
#include "peer_controller.h"
|
|
|
|
#include <library.h>
|
|
#include <utils/debug.h>
|
|
#include <asn1/asn1.h>
|
|
#include <asn1/oid.h>
|
|
#include <utils/identification.h>
|
|
#include <credentials/keys/public_key.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, fast_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=%Y", 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, fast_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,
|
|
fast_request_t *request, char *public_key,
|
|
chunk_t *encoding, chunk_t *keyid)
|
|
{
|
|
public_key_t *public;
|
|
chunk_t blob, id;
|
|
|
|
if (!public_key || *public_key == '\0')
|
|
{
|
|
request->setf(request, "error=Public key is missing.");
|
|
return FALSE;
|
|
}
|
|
blob = chunk_clone(chunk_create(public_key, strlen(public_key)));
|
|
public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
|
|
BUILD_BLOB_PEM, blob,
|
|
BUILD_END);
|
|
chunk_free(&blob);
|
|
if (!public)
|
|
{
|
|
request->setf(request, "error=Parsing public key failed.");
|
|
return FALSE;
|
|
}
|
|
/* TODO: use get_encoding() with an encoding type */
|
|
if (!public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &id) ||
|
|
!public->get_encoding(public, PUBKEY_SPKI_ASN1_DER, encoding))
|
|
{
|
|
request->setf(request, "error=Encoding public key failed.");
|
|
return FALSE;
|
|
}
|
|
*keyid = chunk_clone(id);
|
|
public->destroy(public);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* register a new peer
|
|
*/
|
|
static void add(private_peer_controller_t *this, fast_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, fast_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, fast_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));
|
|
}
|
|
|
|
METHOD(fast_controller_t, get_name, char*,
|
|
private_peer_controller_t *this)
|
|
{
|
|
return "peer";
|
|
}
|
|
|
|
METHOD(fast_controller_t, handle, void,
|
|
private_peer_controller_t *this, fast_request_t *request, char *action,
|
|
char *idstr, char *p3, char *p4, char *p5)
|
|
{
|
|
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");
|
|
}
|
|
|
|
METHOD(fast_controller_t, destroy, void,
|
|
private_peer_controller_t *this)
|
|
{
|
|
free(this);
|
|
}
|
|
|
|
/*
|
|
* see header file
|
|
*/
|
|
fast_controller_t *peer_controller_create(user_t *user, database_t *db)
|
|
{
|
|
private_peer_controller_t *this;
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.controller = {
|
|
.get_name = _get_name,
|
|
.handle = _handle,
|
|
.destroy = _destroy,
|
|
},
|
|
},
|
|
.user = user,
|
|
.db = db,
|
|
);
|
|
|
|
return &this->public.controller;
|
|
}
|