/*
* 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;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>