diff options
Diffstat (limited to 'src/libstrongswan/utils')
-rw-r--r-- | src/libstrongswan/utils/fetcher.c | 421 | ||||
-rw-r--r-- | src/libstrongswan/utils/fetcher.h | 95 | ||||
-rw-r--r-- | src/libstrongswan/utils/host.c | 526 | ||||
-rw-r--r-- | src/libstrongswan/utils/host.h | 231 | ||||
-rw-r--r-- | src/libstrongswan/utils/identification.c | 1144 | ||||
-rw-r--r-- | src/libstrongswan/utils/identification.h | 261 | ||||
-rw-r--r-- | src/libstrongswan/utils/iterator.h | 166 | ||||
-rw-r--r-- | src/libstrongswan/utils/leak_detective.c | 459 | ||||
-rw-r--r-- | src/libstrongswan/utils/leak_detective.h | 35 | ||||
-rw-r--r-- | src/libstrongswan/utils/lexparser.c | 137 | ||||
-rw-r--r-- | src/libstrongswan/utils/lexparser.h | 57 | ||||
-rw-r--r-- | src/libstrongswan/utils/linked_list.c | 763 | ||||
-rw-r--r-- | src/libstrongswan/utils/linked_list.h | 232 | ||||
-rw-r--r-- | src/libstrongswan/utils/randomizer.c | 165 | ||||
-rw-r--r-- | src/libstrongswan/utils/randomizer.h | 114 |
15 files changed, 4806 insertions, 0 deletions
diff --git a/src/libstrongswan/utils/fetcher.c b/src/libstrongswan/utils/fetcher.c new file mode 100644 index 000000000..6165cc1e1 --- /dev/null +++ b/src/libstrongswan/utils/fetcher.c @@ -0,0 +1,421 @@ +/** + * @file fetcher.c + * + * @brief Implementation of fetcher_t. + * + */ + +/* + * Copyright (C) 2007 Andreas Steffen + * 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 <fetcher://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. + */ + +#ifdef LIBCURL +#include <curl/curl.h> +#endif /* LIBCURL */ + +#ifdef LIBLDAP +#include <ldap.h> +#endif /* LIBLDAP */ + +#include <library.h> +#include <debug.h> + +#include "fetcher.h" + +typedef struct private_fetcher_t private_fetcher_t; + +/** + * @brief Private Data of a fetcher_t object. + */ +struct private_fetcher_t { + /** + * Public data + */ + fetcher_t public; + + /** + * URI of the information source + */ + const char *uri; + +#ifdef LIBCURL + /** + * we use libcurl from http://curl.haxx.se/ as a fetcher + */ + CURL* curl; +#endif /* LIBCURL */ + +#ifdef LIBLDAP + /** + * we use libldap from http://www.openssl.org/ as a fetcher + */ + LDAP *ldap; + LDAPURLDesc *lurl; +#endif /* LIBLDAP */ +}; + +/** + * writes data into a dynamically resizeable chunk_t + * needed for libcurl responses + */ +static size_t curl_write_buffer(void *ptr, size_t size, size_t nmemb, void *data) +{ + size_t realsize = size * nmemb; + chunk_t *mem = (chunk_t*)data; + + mem->ptr = (u_char *)realloc(mem->ptr, mem->len + realsize); + if (mem->ptr) { + memcpy(&(mem->ptr[mem->len]), ptr, realsize); + mem->len += realsize; + } + return realsize; +} + +/** + * Implements fetcher_t.get for curl methods + */ +static chunk_t curl_get(private_fetcher_t *this) +{ + chunk_t response = chunk_empty; + +#ifdef LIBCURL + if (this->curl) + { + CURLcode res; + chunk_t curl_response = chunk_empty; + char curl_error_buffer[CURL_ERROR_SIZE]; + + curl_easy_setopt(this->curl, CURLOPT_URL, this->uri); + curl_easy_setopt(this->curl, CURLOPT_WRITEFUNCTION, curl_write_buffer); + curl_easy_setopt(this->curl, CURLOPT_WRITEDATA, (void *)&curl_response); + curl_easy_setopt(this->curl, CURLOPT_ERRORBUFFER, &curl_error_buffer); + curl_easy_setopt(this->curl, CURLOPT_FAILONERROR, TRUE); + curl_easy_setopt(this->curl, CURLOPT_CONNECTTIMEOUT, FETCHER_TIMEOUT); + curl_easy_setopt(this->curl, CURLOPT_NOSIGNAL, TRUE); + + DBG1("sending curl request to '%s'...", this->uri); + res = curl_easy_perform(this->curl); + + if (res == CURLE_OK) + { + DBG1("received valid curl response"); + response = chunk_clone(curl_response); + } + else + { + DBG1("curl request failed: %s", curl_error_buffer); + } + curl_free(curl_response.ptr); + } +#else + DBG1("warning: libcurl fetching not compiled in"); +#endif /* LIBCURL */ + return response; +} + +/** + * Implements fetcher_t.post. + */ +static chunk_t http_post(private_fetcher_t *this, const char *request_type, chunk_t request) +{ + chunk_t response = chunk_empty; + +#ifdef LIBCURL + if (this->curl) + { + CURLcode res; + struct curl_slist *headers = NULL; + chunk_t curl_response = chunk_empty; + char curl_error_buffer[CURL_ERROR_SIZE]; + char content_type[BUF_LEN]; + + /* set content type header */ + snprintf(content_type, BUF_LEN, "Content-Type: %s", request_type); + headers = curl_slist_append(headers, content_type); + + /* set options */ + curl_easy_setopt(this->curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(this->curl, CURLOPT_URL, this->uri); + curl_easy_setopt(this->curl, CURLOPT_WRITEFUNCTION, curl_write_buffer); + curl_easy_setopt(this->curl, CURLOPT_WRITEDATA, (void *)&curl_response); + curl_easy_setopt(this->curl, CURLOPT_POSTFIELDS, request.ptr); + curl_easy_setopt(this->curl, CURLOPT_POSTFIELDSIZE, request.len); + curl_easy_setopt(this->curl, CURLOPT_ERRORBUFFER, &curl_error_buffer); + curl_easy_setopt(this->curl, CURLOPT_FAILONERROR, TRUE); + curl_easy_setopt(this->curl, CURLOPT_CONNECTTIMEOUT, FETCHER_TIMEOUT); + curl_easy_setopt(this->curl, CURLOPT_NOSIGNAL, TRUE); + + DBG1("sending http post request to '%s'...", this->uri); + res = curl_easy_perform(this->curl); + + if (res == CURLE_OK) + { + DBG1("received valid http response"); + response = chunk_clone(curl_response); + } + else + { + DBG1("http post request using libcurl failed: %s", curl_error_buffer); + } + curl_slist_free_all(headers); + curl_free(curl_response.ptr); + } +#else + DBG1("warning: libcurl fetching not compiled in"); +#endif /* LIBCURL */ + return response; +} + +#ifdef LIBLDAP +/** + * Parses the result returned by an ldap query + */ +static chunk_t ldap_parse(LDAP *ldap, LDAPMessage *result) +{ + chunk_t response = chunk_empty; + err_t ugh = NULL; + + LDAPMessage *entry = ldap_first_entry(ldap, result); + + if (entry != NULL) + { + BerElement *ber = NULL; + char *attr; + + attr = ldap_first_attribute(ldap, entry, &ber); + + if (attr != NULL) + { + struct berval **values = ldap_get_values_len(ldap, entry, attr); + + if (values != NULL) + { + if (values[0] != NULL) + { + response.len = values[0]->bv_len; + response.ptr = malloc(response.len); + memcpy(response.ptr, values[0]->bv_val, response.len); + + if (values[1] != NULL) + { + ugh = "more than one value was fetched - first selected"; + } + } + else + { + ugh = "no values in attribute"; + } + ldap_value_free_len(values); + } + else + { + ugh = ldap_err2string(ldap_result2error(ldap, entry, 0)); + } + ldap_memfree(attr); + } + else + { + ugh = ldap_err2string(ldap_result2error(ldap, entry, 0)); + } + ber_free(ber, 0); + } + else + { + ugh = ldap_err2string(ldap_result2error(ldap, result, 0)); + } + if (ugh) + { + DBG1("ldap request failed: %s", ugh); + } + return response; +} +#endif /* LIBLDAP */ + +/** + * Implements fetcher_t.get for curl methods + */ +static chunk_t ldap_get(private_fetcher_t *this) +{ + chunk_t response = chunk_empty; + +#ifdef LIBLDAP + if (this->ldap) + { + err_t ugh = NULL; + int rc; + int ldap_version = LDAP_VERSION3; + + struct timeval timeout; + + timeout.tv_sec = FETCHER_TIMEOUT; + timeout.tv_usec = 0; + + ldap_set_option(this->ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version); + ldap_set_option(this->ldap, LDAP_OPT_NETWORK_TIMEOUT, &timeout); + + DBG1("sending ldap request to '%s'...", this->uri); + + rc = ldap_simple_bind_s(this->ldap, NULL, NULL); + if (rc == LDAP_SUCCESS) + { + LDAPMessage *result; + + timeout.tv_sec = FETCHER_TIMEOUT; + timeout.tv_usec = 0; + + rc = ldap_search_st(this->ldap, this->lurl->lud_dn, + this->lurl->lud_scope, + this->lurl->lud_filter, + this->lurl->lud_attrs, + 0, &timeout, &result); + + if (rc == LDAP_SUCCESS) + { + response = ldap_parse(this->ldap, result); + if (response.ptr) + { + DBG1("received valid ldap response"); + } + ldap_msgfree(result); + } + else + { + ugh = ldap_err2string(rc); + } + } + else + { + ugh = ldap_err2string(rc); + } + ldap_unbind_s(this->ldap); + + if (ugh) + { + DBG1("ldap request failed: %s", ugh); + } + } +#else /* !LIBLDAP */ + DBG1("warning: libldap fetching not compiled in"); +#endif /* !LIBLDAP */ + return response; +} + +/** + * Implements fetcher_t.destroy + */ +static void destroy(private_fetcher_t *this) +{ +#ifdef LIBCURL + if (this->curl) + { + curl_easy_cleanup(this->curl); + } +#endif /* LIBCURL */ + +#ifdef LIBLDAP + if (this->lurl) + { + ldap_free_urldesc(this->lurl); + } +#endif /* LIBLDAP */ + + free(this); +} + +/* + * Described in header. + */ +fetcher_t *fetcher_create(const char *uri) +{ + private_fetcher_t *this = malloc_thing(private_fetcher_t); + + /* initialize */ + this->uri = uri; + +#ifdef LIBCURL + this->curl = NULL; +#endif /* LIBCURL */ + +#ifdef LIBLDAP + this->lurl = NULL; + this->ldap = NULL; +#endif /* LIBLDAP */ + + if (strlen(uri) >= 4 && strncasecmp(uri, "ldap", 4) == 0) + { +#ifdef LIBLDAP + int rc = ldap_url_parse(uri, &this->lurl); + + if (rc == LDAP_SUCCESS) + { + this->ldap = ldap_init(this->lurl->lud_host, + this->lurl->lud_port); + } + else + { + DBG1("ldap: %s", ldap_err2string(rc)); + this->ldap = NULL; + } +#endif /* LIBLDAP */ + this->public.get = (chunk_t (*) (fetcher_t*))ldap_get; + } + else + { +#ifdef LIBCURL + this->curl = curl_easy_init(); + if (this->curl == NULL) + { + DBG1("curl_easy_init_failed()"); + } +#endif /* LIBCURL */ + this->public.get = (chunk_t (*) (fetcher_t*))curl_get; + } + + /* public functions */ + this->public.post = (chunk_t (*) (fetcher_t*,const char*,chunk_t))http_post; + this->public.destroy = (void (*) (fetcher_t*))destroy; + + return &this->public; +} + +/** + * Described in header. + */ +void fetcher_initialize(void) +{ +#ifdef LIBCURL + CURLcode res; + + /* initialize libcurl */ + DBG1("initializing libcurl"); + res = curl_global_init(CURL_GLOBAL_NOTHING); + if (res != CURLE_OK) + { + DBG1("libcurl could not be initialized: %s", curl_easy_strerror(res)); + } +#endif /* LIBCURL */ +} + +/** + * Described in header. + */ +void fetcher_finalize(void) +{ +#ifdef LIBCURL + /* finalize libcurl */ + DBG1("finalizing libcurl"); + curl_global_cleanup(); +#endif /* LIBCURL */ +} + diff --git a/src/libstrongswan/utils/fetcher.h b/src/libstrongswan/utils/fetcher.h new file mode 100644 index 000000000..47b43a0b7 --- /dev/null +++ b/src/libstrongswan/utils/fetcher.h @@ -0,0 +1,95 @@ +/** + * @file fetcher.h + * + * @brief Interface of fetcher_t. + * + */ + +/* + * Copyright (C) 2007 Andreas Steffen + * 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 <fetcher://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. + */ + +#ifndef FETCHER_H_ +#define FETCHER_H_ + +typedef struct fetcher_t fetcher_t; + +#include <chunk.h> + +#define FETCHER_TIMEOUT 10 /* seconds */ + +/** + * @brief Fetches information from an URI (http, file, ftp, etc.) + * + * @ingroup utils + */ +struct fetcher_t { + + /** + * @brief Get information via a get request. + * + * @param this calling object + * @param uri uri specifying the information source + * @return chunk_t containing the information + */ + chunk_t (*get) (fetcher_t *this); + + /** + * @brief Get information via a get request. + * + * @param this calling object + * @param uri uri specifying the information source + * @param type content type of http post request + * @param request binary data for http post request + * @return chunk_t containing the information + */ + chunk_t (*post) (fetcher_t *this, const char *type, chunk_t request); + + /** + * @brief Destroys the fetcher_t object. + * + * @param this fetcher_t to destroy + */ + void (*destroy) (fetcher_t *this); + +}; + +/** + * @brief Create a fetcher_t object. + * + * @return created fetcher_t object + * + * @ingroup utils + */ +fetcher_t* fetcher_create(const char *uri); + +/** + * @brief Initializes the fetcher_t class + * + * call this function only once in the main program + * + * @ingroup utils + */ +void fetcher_initialize(void); + +/** + * @brief Finalizes the fetcher_t class + * + * call this function only once befor exiting the main program + * + * @ingroup utils + */ +void fetcher_finalize(void); + +#endif /*FETCHER_H_*/ diff --git a/src/libstrongswan/utils/host.c b/src/libstrongswan/utils/host.c new file mode 100644 index 000000000..8cbfd6ab8 --- /dev/null +++ b/src/libstrongswan/utils/host.c @@ -0,0 +1,526 @@ +/** + * @file host.c + * + * @brief Implementation of host_t. + * + */ + +/* + * Copyright (C) 2006-2007 Tobias Brunner + * Copyright (C) 2006 Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#include <string.h> +#include <printf.h> + +#include "host.h" + + +typedef struct private_host_t private_host_t; + +/** + * @brief Private Data of a host object. + */ +struct private_host_t { + /** + * Public data + */ + host_t public; + + /** + * low-lewel structure, wich stores the address + */ + union { + /** generic type */ + struct sockaddr address; + /** maximum sockaddr size */ + struct sockaddr_storage address_max; + /** IPv4 address */ + struct sockaddr_in address4; + /** IPv6 address */ + struct sockaddr_in6 address6; + }; + /** + * length of address structure + */ + socklen_t socklen; +}; + + +/** + * implements host_t.get_sockaddr + */ +static sockaddr_t *get_sockaddr(private_host_t *this) +{ + return &(this->address); +} + +/** + * implements host_t.get_sockaddr_len + */ +static socklen_t *get_sockaddr_len(private_host_t *this) +{ + return &(this->socklen); +} + +/** + * Implementation of host_t.is_anyaddr. + */ +static bool is_anyaddr(private_host_t *this) +{ + switch (this->address.sa_family) + { + case AF_INET: + { + u_int8_t default_route[4]; + memset(default_route, 0, sizeof(default_route)); + return memeq(default_route, &(this->address4.sin_addr.s_addr), + sizeof(default_route)); + } + case AF_INET6: + { + u_int8_t default_route[16]; + memset(default_route, 0, sizeof(default_route)); + return memeq(default_route, &(this->address6.sin6_addr.s6_addr), + sizeof(default_route)); + } + default: + { + return FALSE; + } + } +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_host_t *this = *((private_host_t**)(args[0])); + char buffer[INET6_ADDRSTRLEN]; + void *address; + u_int16_t port; + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + if (is_anyaddr(this)) + { + return fprintf(stream, "%%any"); + } + + switch (this->address.sa_family) + { + case AF_INET: + address = &this->address4.sin_addr; + port = this->address4.sin_port; + break; + case AF_INET6: + address = &this->address6.sin6_addr; + port = this->address6.sin6_port; + break; + default: + return fprintf(stream, "(family not supported)"); + } + + if (inet_ntop(this->address.sa_family, address, + buffer, sizeof(buffer)) == NULL) + { + return fprintf(stream, "(address conversion failed)"); + } + + if (info->alt) + { + return fprintf(stream, "%s[%d]", buffer, ntohs(port)); + } + else + { + return fprintf(stream, "%s", buffer); + } +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_HOST, print, arginfo_ptr); +} + +/** + * Implementation of host_t.get_address. + */ +static chunk_t get_address(private_host_t *this) +{ + chunk_t address = chunk_empty; + + switch (this->address.sa_family) + { + case AF_INET: + { + address.ptr = (char*)&(this->address4.sin_addr.s_addr); + address.len = 4; + return address; + } + case AF_INET6: + { + address.ptr = (char*)&(this->address6.sin6_addr.s6_addr); + address.len = 16; + return address; + } + default: + { + /* return empty chunk */ + return address; + } + } +} + +/** + * implements host_t.get_family + */ +static int get_family(private_host_t *this) +{ + return this->address.sa_family; +} + +/** + * implements host_t.get_port + */ +static u_int16_t get_port(private_host_t *this) +{ + switch (this->address.sa_family) + { + case AF_INET: + { + return ntohs(this->address4.sin_port); + } + case AF_INET6: + { + return ntohs(this->address6.sin6_port); + } + default: + { + return 0; + } + } +} + +/** + * implements host_t.set_port + */ +static void set_port(private_host_t *this, u_int16_t port) +{ + switch (this->address.sa_family) + { + case AF_INET: + { + this->address4.sin_port = htons(port); + break; + } + case AF_INET6: + { + this->address6.sin6_port = htons(port); + break; + } + default: + { + break; + } + } +} + +/** + * Implements host_t.clone. + */ +static private_host_t *clone_(private_host_t *this) +{ + private_host_t *new = malloc_thing(private_host_t); + + memcpy(new, this, sizeof(private_host_t)); + return new; +} + +/** + * Impelements host_t.ip_equals + */ +static bool ip_equals(private_host_t *this, private_host_t *other) +{ + if (this->address.sa_family != other->address.sa_family) + { + /* 0.0.0.0 and ::0 are equal */ + if (is_anyaddr(this) && is_anyaddr(other)) + { + return TRUE; + } + + return FALSE; + } + + switch (this->address.sa_family) + { + case AF_INET: + { + if (memeq(&this->address4.sin_addr, &other->address4.sin_addr, + sizeof(this->address4.sin_addr))) + { + return TRUE; + } + break; + } + case AF_INET6: + { + if (memeq(&this->address6.sin6_addr, &other->address6.sin6_addr, + sizeof(this->address6.sin6_addr))) + { + return TRUE; + } + } + default: + break; + } + return FALSE; +} + +/** + * Implements host_t.get_differences + */ +static host_diff_t get_differences(host_t *this, host_t *other) +{ + host_diff_t ret = HOST_DIFF_NONE; + + if (!this->ip_equals(this, other)) + { + ret |= HOST_DIFF_ADDR; + } + + if (this->get_port(this) != other->get_port(other)) + { + ret |= HOST_DIFF_PORT; + } + + return ret; +} + +/** + * Impelements host_t.equals + */ +static bool equals(private_host_t *this, private_host_t *other) +{ + if (!ip_equals(this, other)) + { + return FAILED; + } + + switch (this->address.sa_family) + { + case AF_INET: + { + if (this->address4.sin_port == other->address4.sin_port) + { + return TRUE; + } + break; + } + case AF_INET6: + { + if (this->address6.sin6_port == other->address6.sin6_port) + { + return TRUE; + } + break; + } + default: + break; + } + return FALSE; +} + +/** + * Implements host_t.destroy + */ +static void destroy(private_host_t *this) +{ + free(this); +} + +/** + * Creates an empty host_t object + */ +static private_host_t *host_create_empty(void) +{ + private_host_t *this = malloc_thing(private_host_t); + + this->public.get_sockaddr = (sockaddr_t* (*) (host_t*))get_sockaddr; + this->public.get_sockaddr_len = (socklen_t*(*) (host_t*))get_sockaddr_len; + this->public.clone = (host_t* (*) (host_t*))clone_; + this->public.get_family = (int (*) (host_t*))get_family; + this->public.get_address = (chunk_t (*) (host_t *)) get_address; + this->public.get_port = (u_int16_t (*) (host_t *))get_port; + this->public.set_port = (void (*) (host_t *,u_int16_t))set_port; + this->public.get_differences = get_differences; + this->public.ip_equals = (bool (*) (host_t *,host_t *)) ip_equals; + this->public.equals = (bool (*) (host_t *,host_t *)) equals; + this->public.is_anyaddr = (bool (*) (host_t *)) is_anyaddr; + this->public.destroy = (void (*) (host_t*))destroy; + + return this; +} + +/* + * Described in header. + */ +host_t *host_create_from_string(char *string, u_int16_t port) +{ + private_host_t *this = host_create_empty(); + + if (strchr(string, '.')) + { + this->address.sa_family = AF_INET; + } + else + { + this->address.sa_family = AF_INET6; + } + + switch (this->address.sa_family) + { + case AF_INET: + { + if (inet_pton(AF_INET, string, &this->address4.sin_addr) <=0) + { + break; + } + this->address4.sin_port = htons(port); + this->socklen = sizeof(struct sockaddr_in); + return &this->public; + } + case AF_INET6: + { + if (inet_pton(AF_INET6, string, &this->address6.sin6_addr) <=0) + { + break; + } + this->address6.sin6_port = htons(port); + this->socklen = sizeof(struct sockaddr_in6); + return &this->public; + } + default: + { + break; + } + } + free(this); + return NULL; +} + +/* + * Described in header. + */ +host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port) +{ + private_host_t *this = host_create_empty(); + + this->address.sa_family = family; + switch (family) + { + case AF_INET: + { + if (address.len != 4) + { + break; + } + memcpy(&(this->address4.sin_addr.s_addr), address.ptr,4); + this->address4.sin_port = htons(port); + this->socklen = sizeof(struct sockaddr_in); + return &(this->public); + } + case AF_INET6: + { + if (address.len != 16) + { + break; + } + memcpy(&(this->address6.sin6_addr.s6_addr), address.ptr, 16); + this->address6.sin6_port = htons(port); + this->socklen = sizeof(struct sockaddr_in6); + return &this->public; + } + default: + break; + } + free(this); + return NULL; +} + +/* + * Described in header. + */ +host_t *host_create_from_sockaddr(sockaddr_t *sockaddr) +{ + private_host_t *this = host_create_empty(); + + switch (sockaddr->sa_family) + { + case AF_INET: + { + memcpy(&this->address4, sockaddr, sizeof(struct sockaddr_in)); + this->socklen = sizeof(struct sockaddr_in); + return &this->public; + } + case AF_INET6: + { + memcpy(&this->address6, sockaddr, sizeof(struct sockaddr_in6)); + this->socklen = sizeof(struct sockaddr_in6); + return &this->public; + } + default: + break; + } + free(this); + return NULL; +} + +/* + * Described in header. + */ +host_t *host_create_any(int family) +{ + private_host_t *this = host_create_empty(); + + memset(&this->address_max, 0, sizeof(struct sockaddr_storage)); + this->address.sa_family = family; + + switch (family) + { + case AF_INET: + { + this->socklen = sizeof(struct sockaddr_in); + return &(this->public); + } + case AF_INET6: + { + this->socklen = sizeof(struct sockaddr_in6); + return &this->public; + } + default: + break; + } + return NULL; +} diff --git a/src/libstrongswan/utils/host.h b/src/libstrongswan/utils/host.h new file mode 100644 index 000000000..ee9aa457f --- /dev/null +++ b/src/libstrongswan/utils/host.h @@ -0,0 +1,231 @@ +/** + * @file host.h + * + * @brief Interface of host_t. + * + */ + +/* + * Copyright (C) 2006-2007 Tobias Brunner + * Copyright (C) 2006 Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef HOST_H_ +#define HOST_H_ + +typedef enum host_diff_t host_diff_t; +typedef struct host_t host_t; + +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <library.h> + +/** + * Differences between two hosts. They differ in + * address, port, or both. + */ +enum host_diff_t { + HOST_DIFF_NONE = 0, + HOST_DIFF_ADDR = 1, + HOST_DIFF_PORT = 2, +}; + +/** + * @brief Representates a Host + * + * Host object, identifies a address:port pair and defines some + * useful functions on it. + * + * @b Constructors: + * - host_create() + * - host_create_from_chunk() + * - host_create_from_sockaddr() + * + * @todo Add IPv6 support + * + * @ingroup utils + */ +struct host_t { + + /** + * @brief Build a clone of this host object. + * + * @param this object to clone + * @return cloned host + */ + host_t *(*clone) (host_t *this); + + /** + * @brief Get a pointer to the internal sockaddr struct. + * + * This is used for sending and receiving via sockets. + * + * @param this object to clone + * @return pointer to the internal sockaddr structure + */ + sockaddr_t *(*get_sockaddr) (host_t *this); + + /** + * @brief Get the length of the sockaddr struct. + * + * Depending on the family, the length of the sockaddr struct + * is different. Use this function to get the length of the sockaddr + * struct returned by get_sock_addr. + * + * This is used for sending and receiving via sockets. + * + * @param this object to clone + * @return length of the sockaddr struct + */ + socklen_t *(*get_sockaddr_len) (host_t *this); + + /** + * @brief Gets the family of the address + * + * @param this calling object + * @return family + */ + int (*get_family) (host_t *this); + + /** + * @brief Checks if the ip address of host is set to default route. + * + * @param this calling object + * @return + * - TRUE if host has IP 0.0.0.0 for default route + * - FALSE otherwise + */ + bool (*is_anyaddr) (host_t *this); + + /** + * @brief get the address of this host as chunk_t + * + * Returned chunk points to internal data. + * + * @param this object + * @return address string, + */ + chunk_t (*get_address) (host_t *this); + + /** + * @brief get the port of this host + * + * @param this object to clone + * @return port number + */ + u_int16_t (*get_port) (host_t *this); + + /** + * @brief set the port of this host + * + * @param this object to clone + * @param port port numer + */ + void (*set_port) (host_t *this, u_int16_t port); + + /** + * @brief Compare the ips of two hosts hosts. + * + * @param this object to compare + * @param other the other to compare + * @return TRUE if addresses are equal. + */ + bool (*ip_equals) (host_t *this, host_t *other); + + /** + * @brief Compare two hosts, with port. + * + * @param this object to compare + * @param other the other to compare + * @return TRUE if addresses and ports are equal. + */ + bool (*equals) (host_t *this, host_t *other); + + /** + * @brief Compare two hosts and return the differences. + * + * @param this object to compare + * @param other the other to compare + * @return differences in a combination of host_diff_t's + */ + host_diff_t (*get_differences) (host_t *this, host_t *other); + + /** + * @brief Destroy this host object + * + * @param this calling + * @return SUCCESS in any case + */ + void (*destroy) (host_t *this); +}; + +/** + * @brief Constructor to create a host_t object from an address string. + * + * @param string string of an address, such as "152.96.193.130" + * @param port port number + * @return + * - host_t object + * - NULL, if string not an address. + * + * @ingroup network + */ +host_t *host_create_from_string(char *string, u_int16_t port); + +/** + * @brief Constructor to create a host_t object from an address chunk + * + * @param family Address family to use for this object, such as AF_INET or AF_INET6 + * @param address address as 4 byte chunk_t in networ order + * @param port port number + * @return + * - host_t object + * - NULL, if family not supported or chunk_t length not 4 bytes. + * + * @ingroup network + */ +host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port); + +/** + * @brief Constructor to create a host_t object from a sockaddr struct + * + * @param sockaddr sockaddr struct which contains family, address and port + * @return + * - host_t object + * - NULL, if family not supported. + * + * @ingroup network + */ +host_t *host_create_from_sockaddr(sockaddr_t *sockaddr); + +/** + * @brief Create a host without an address, a "any" host. + * + * @param family family of the any host + * @return + * - host_t object + * - NULL, if family not supported. + * + * @ingroup network + */ +host_t *host_create_any(int family); + +#endif /*HOST_H_*/ diff --git a/src/libstrongswan/utils/identification.c b/src/libstrongswan/utils/identification.c new file mode 100644 index 000000000..341af39c0 --- /dev/null +++ b/src/libstrongswan/utils/identification.c @@ -0,0 +1,1144 @@ +/** + * @file identification.c + * + * @brief Implementation of identification_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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 <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <printf.h> + +#include "identification.h" + +#include <asn1/asn1.h> + +ENUM_BEGIN(id_type_names, ID_ANY, ID_KEY_ID, + "ID_ANY", + "ID_IPV4_ADDR", + "ID_FQDN", + "ID_RFC822_ADDR", + "ID_IPV4_ADDR_SUBNET", + "ID_IPV6_ADDR", + "ID_IPV6_ADDR_SUBNET", + "ID_IPV4_ADDR_RANGE", + "ID_IPV6_ADDR_RANGE", + "ID_DER_ASN1_DN", + "ID_DER_ASN1_GN", + "ID_KEY_ID"); +ENUM_NEXT(id_type_names, ID_DER_ASN1_GN_URI, ID_DER_ASN1_GN_URI, ID_KEY_ID, + "ID_DER_ASN1_GN_URI"); +ENUM_END(id_type_names, ID_DER_ASN1_GN_URI); + + +/** + * X.501 acronyms for well known object identifiers (OIDs) + */ +static u_char oid_ND[] = { + 0x02, 0x82, 0x06, 0x01, 0x0A, 0x07, 0x14 +}; +static u_char oid_UID[] = { + 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x01 +}; +static u_char oid_DC[] = { + 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 +}; +static u_char oid_CN[] = { + 0x55, 0x04, 0x03 +}; +static u_char oid_S[] = { + 0x55, 0x04, 0x04 +}; +static u_char oid_SN[] = { + 0x55, 0x04, 0x05 +}; +static u_char oid_C[] = { + 0x55, 0x04, 0x06 +}; +static u_char oid_L[] = { + 0x55, 0x04, 0x07 +}; +static u_char oid_ST[] = { + 0x55, 0x04, 0x08 +}; +static u_char oid_O[] = { + 0x55, 0x04, 0x0A +}; +static u_char oid_OU[] = { + 0x55, 0x04, 0x0B +}; +static u_char oid_T[] = { + 0x55, 0x04, 0x0C +}; +static u_char oid_D[] = { + 0x55, 0x04, 0x0D +}; +static u_char oid_N[] = { + 0x55, 0x04, 0x29 +}; +static u_char oid_G[] = { + 0x55, 0x04, 0x2A +}; +static u_char oid_I[] = { + 0x55, 0x04, 0x2B +}; +static u_char oid_ID[] = { + 0x55, 0x04, 0x2D +}; +static u_char oid_EN[] = { + 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x42, 0x03, 0x01, 0x03 +}; +static u_char oid_E[] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01 +}; +static u_char oid_UN[] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x02 +}; +static u_char oid_TCGID[] = { + 0x2B, 0x06, 0x01, 0x04, 0x01, 0x89, 0x31, 0x01, 0x01, 0x02, 0x02, 0x4B +}; + +/** + * coding of X.501 distinguished name + */ +typedef struct { + const u_char *name; + chunk_t oid; + u_char type; +} x501rdn_t; + +static const x501rdn_t x501rdns[] = { + {"ND", {oid_ND, 7}, ASN1_PRINTABLESTRING}, + {"UID", {oid_UID, 10}, ASN1_PRINTABLESTRING}, + {"DC", {oid_DC, 10}, ASN1_PRINTABLESTRING}, + {"CN", {oid_CN, 3}, ASN1_PRINTABLESTRING}, + {"S", {oid_S, 3}, ASN1_PRINTABLESTRING}, + {"SN", {oid_SN, 3}, ASN1_PRINTABLESTRING}, + {"serialNumber", {oid_SN, 3}, ASN1_PRINTABLESTRING}, + {"C", {oid_C, 3}, ASN1_PRINTABLESTRING}, + {"L", {oid_L, 3}, ASN1_PRINTABLESTRING}, + {"ST", {oid_ST, 3}, ASN1_PRINTABLESTRING}, + {"O", {oid_O, 3}, ASN1_PRINTABLESTRING}, + {"OU", {oid_OU, 3}, ASN1_PRINTABLESTRING}, + {"T", {oid_T, 3}, ASN1_PRINTABLESTRING}, + {"D", {oid_D, 3}, ASN1_PRINTABLESTRING}, + {"N", {oid_N, 3}, ASN1_PRINTABLESTRING}, + {"G", {oid_G, 3}, ASN1_PRINTABLESTRING}, + {"I", {oid_I, 3}, ASN1_PRINTABLESTRING}, + {"ID", {oid_ID, 3}, ASN1_PRINTABLESTRING}, + {"EN", {oid_EN, 10}, ASN1_PRINTABLESTRING}, + {"employeeNumber", {oid_EN, 10}, ASN1_PRINTABLESTRING}, + {"E", {oid_E, 9}, ASN1_IA5STRING}, + {"Email", {oid_E, 9}, ASN1_IA5STRING}, + {"emailAddress", {oid_E, 9}, ASN1_IA5STRING}, + {"UN", {oid_UN, 9}, ASN1_IA5STRING}, + {"unstructuredName",{oid_UN, 9}, ASN1_IA5STRING}, + {"TCGID", {oid_TCGID, 12}, ASN1_PRINTABLESTRING} +}; +#define X501_RDN_ROOF 26 + +/** + * maximum number of RDNs in atodn() + */ +#define RDN_MAX 20 + + +typedef struct private_identification_t private_identification_t; + +/** + * Private data of an identification_t object. + */ +struct private_identification_t { + /** + * Public interface. + */ + identification_t public; + + /** + * Encoded representation of this ID. + */ + chunk_t encoded; + + /** + * Type of this ID. + */ + id_type_t type; +}; + +static private_identification_t *identification_create(void); + +/** + * updates a chunk (!????) + * TODO: We should reconsider this stuff, its not really clear + */ +static void update_chunk(chunk_t *ch, int n) +{ + n = (n > -1 && n < (int)ch->len)? n : (int)ch->len-1; + ch->ptr += n; ch->len -= n; +} + +/** + * Prints a binary string in hexadecimal form + */ +void hex_str(chunk_t bin, chunk_t *str) +{ + u_int i; + update_chunk(str, snprintf(str->ptr,str->len,"0x")); + for (i = 0; i < bin.len; i++) + { + update_chunk(str, snprintf(str->ptr,str->len,"%02X",*bin.ptr++)); + } +} + +/** + * Remove any malicious characters from a chunk. We are very restrictive, but + * whe use these strings only to present it to the user. + */ +static chunk_t sanitize_chunk(chunk_t chunk) +{ + char *pos; + chunk_t clone = chunk_clone(chunk); + + for (pos = clone.ptr; pos < (char*)(clone.ptr + clone.len); pos++) + { + switch (*pos) + { + case '\0': + case ' ': + case '*': + case '-': + case '.': + case '/': + case '0' ... '9': + case ':': + case '=': + case '@': + case 'A' ... 'Z': + case '_': + case 'a' ... 'z': + break; + default: + *pos = '?'; + } + } + return clone; +} + +/** + * Pointer is set to the first RDN in a DN + */ +static status_t init_rdn(chunk_t dn, chunk_t *rdn, chunk_t *attribute, bool *next) +{ + *rdn = chunk_empty; + *attribute = chunk_empty; + + /* a DN is a SEQUENCE OF RDNs */ + if (*dn.ptr != ASN1_SEQUENCE) + { + /* DN is not a SEQUENCE */ + return FAILED; + } + + rdn->len = asn1_length(&dn); + + if (rdn->len == ASN1_INVALID_LENGTH) + { + /* Invalid RDN length */ + return FAILED; + } + + rdn->ptr = dn.ptr; + + /* are there any RDNs ? */ + *next = rdn->len > 0; + + return SUCCESS; +} + +/** + * Fetches the next RDN in a DN + */ +static status_t get_next_rdn(chunk_t *rdn, chunk_t * attribute, chunk_t *oid, chunk_t *value, asn1_t *type, bool *next) +{ + chunk_t body; + + /* initialize return values */ + *oid = chunk_empty; + *value = chunk_empty; + + /* if all attributes have been parsed, get next rdn */ + if (attribute->len <= 0) + { + /* an RDN is a SET OF attributeTypeAndValue */ + if (*rdn->ptr != ASN1_SET) + { + /* RDN is not a SET */ + return FAILED; + } + attribute->len = asn1_length(rdn); + if (attribute->len == ASN1_INVALID_LENGTH) + { + /* Invalid attribute length */ + return FAILED; + } + attribute->ptr = rdn->ptr; + /* advance to start of next RDN */ + rdn->ptr += attribute->len; + rdn->len -= attribute->len; + } + + /* an attributeTypeAndValue is a SEQUENCE */ + if (*attribute->ptr != ASN1_SEQUENCE) + { + /* attributeTypeAndValue is not a SEQUENCE */ + return FAILED; + } + + /* extract the attribute body */ + body.len = asn1_length(attribute); + + if (body.len == ASN1_INVALID_LENGTH) + { + /* Invalid attribute body length */ + return FAILED; + } + + body.ptr = attribute->ptr; + + /* advance to start of next attribute */ + attribute->ptr += body.len; + attribute->len -= body.len; + + /* attribute type is an OID */ + if (*body.ptr != ASN1_OID) + { + /* attributeType is not an OID */ + return FAILED; + } + /* extract OID */ + oid->len = asn1_length(&body); + + if (oid->len == ASN1_INVALID_LENGTH) + { + /* Invalid attribute OID length */ + return FAILED; + } + oid->ptr = body.ptr; + + /* advance to the attribute value */ + body.ptr += oid->len; + body.len -= oid->len; + + /* extract string type */ + *type = *body.ptr; + + /* extract string value */ + value->len = asn1_length(&body); + + if (value->len == ASN1_INVALID_LENGTH) + { + /* Invalid attribute string length */ + return FAILED; + } + value->ptr = body.ptr; + + /* are there any RDNs left? */ + *next = rdn->len > 0 || attribute->len > 0; + return SUCCESS; +} + +/** + * Parses an ASN.1 distinguished name int its OID/value pairs + */ +static status_t dntoa(chunk_t dn, chunk_t *str) +{ + chunk_t rdn, oid, attribute, value, proper; + asn1_t type; + int oid_code; + bool next; + bool first = TRUE; + + status_t status = init_rdn(dn, &rdn, &attribute, &next); + + if (status != SUCCESS) + return status; + + while (next) + { + status = get_next_rdn(&rdn, &attribute, &oid, &value, &type, &next); + + if (status != SUCCESS) + return status; + + if (first) + { /* first OID/value pair */ + first = FALSE; + } + else + { /* separate OID/value pair by a comma */ + update_chunk(str, snprintf(str->ptr,str->len,", ")); + } + + /* print OID */ + oid_code = known_oid(oid); + if (oid_code == OID_UNKNOWN) + { /* OID not found in list */ + hex_str(oid, str); + } + else + { + update_chunk(str, snprintf(str->ptr,str->len,"%s", oid_names[oid_code].name)); + } + /* print value */ + proper = sanitize_chunk(value); + update_chunk(str, snprintf(str->ptr,str->len,"=%.*s", (int)proper.len, proper.ptr)); + chunk_free(&proper); + } + return SUCCESS; +} + +/** + * compare two distinguished names by + * comparing the individual RDNs + */ +static bool same_dn(chunk_t a, chunk_t b) +{ + chunk_t rdn_a, rdn_b, attribute_a, attribute_b; + chunk_t oid_a, oid_b, value_a, value_b; + asn1_t type_a, type_b; + bool next_a, next_b; + + /* same lengths for the DNs */ + if (a.len != b.len) + return FALSE; + + /* try a binary comparison first */ + if (memeq(a.ptr, b.ptr, b.len)) + return TRUE; + + /* initialize DN parsing */ + if (init_rdn(a, &rdn_a, &attribute_a, &next_a) != SUCCESS + || init_rdn(b, &rdn_b, &attribute_b, &next_b) != SUCCESS) + { + return FALSE; + } + + /* fetch next RDN pair */ + while (next_a && next_b) + { + /* parse next RDNs and check for errors */ + if (get_next_rdn(&rdn_a, &attribute_a, &oid_a, &value_a, &type_a, &next_a) != SUCCESS + || get_next_rdn(&rdn_b, &attribute_b, &oid_b, &value_b, &type_b, &next_b) != SUCCESS) + { + return FALSE; + } + + /* OIDs must agree */ + if (oid_a.len != oid_b.len || memcmp(oid_a.ptr, oid_b.ptr, oid_b.len) != 0) + return FALSE; + + /* same lengths for values */ + if (value_a.len != value_b.len) + return FALSE; + + /* printableStrings and email RDNs require uppercase comparison */ + if (type_a == type_b && (type_a == ASN1_PRINTABLESTRING + || (type_a == ASN1_IA5STRING && known_oid(oid_a) == OID_PKCS9_EMAIL))) + { + if (strncasecmp(value_a.ptr, value_b.ptr, value_b.len) != 0) + return FALSE; + } + else + { + if (strncmp(value_a.ptr, value_b.ptr, value_b.len) != 0) + return FALSE; + } + } + /* both DNs must have same number of RDNs */ + if (next_a || next_b) + return FALSE; + + /* the two DNs are equal! */ + return TRUE; +} + + +/** + * compare two distinguished names by comparing the individual RDNs. + * A single'*' character designates a wildcard RDN in DN b. + * TODO: Add support for different RDN order in DN !! + */ +bool match_dn(chunk_t a, chunk_t b, int *wildcards) +{ + chunk_t rdn_a, rdn_b, attribute_a, attribute_b; + chunk_t oid_a, oid_b, value_a, value_b; + asn1_t type_a, type_b; + bool next_a, next_b; + + /* initialize wildcard counter */ + if (wildcards) + { + *wildcards = 0; + } + + /* initialize DN parsing */ + if (init_rdn(a, &rdn_a, &attribute_a, &next_a) != SUCCESS + || init_rdn(b, &rdn_b, &attribute_b, &next_b) != SUCCESS) + { + return FALSE; + } + + /* fetch next RDN pair */ + while (next_a && next_b) + { + /* parse next RDNs and check for errors */ + if (get_next_rdn(&rdn_a, &attribute_a, &oid_a, &value_a, &type_a, &next_a) != SUCCESS + || get_next_rdn(&rdn_b, &attribute_b, &oid_b, &value_b, &type_b, &next_b) != SUCCESS) + { + return FALSE; + } + /* OIDs must agree */ + if (oid_a.len != oid_b.len || memcmp(oid_a.ptr, oid_b.ptr, oid_b.len) != 0) + return FALSE; + + /* does rdn_b contain a wildcard? */ + if (value_b.len == 1 && *value_b.ptr == '*') + { + if (wildcards) + { + (*wildcards)++; + } + continue; + } + /* same lengths for values */ + if (value_a.len != value_b.len) + return FALSE; + + /* printableStrings and email RDNs require uppercase comparison */ + if (type_a == type_b && (type_a == ASN1_PRINTABLESTRING + || (type_a == ASN1_IA5STRING && known_oid(oid_a) == OID_PKCS9_EMAIL))) + { + if (strncasecmp(value_a.ptr, value_b.ptr, value_b.len) != 0) + return FALSE; + } + else + { + if (strncmp(value_a.ptr, value_b.ptr, value_b.len) != 0) + return FALSE; + } + } + /* both DNs must have same number of RDNs */ + if (next_a || next_b) + { + return FALSE; + } + + /* the two DNs match! */ + if (wildcards) + { + *wildcards = min(*wildcards, MAX_WILDCARDS); + } + return TRUE; +} + +/** + * Converts an LDAP-style human-readable ASCII-encoded + * ASN.1 distinguished name into binary DER-encoded format + */ +static status_t atodn(char *src, chunk_t *dn) +{ + /* finite state machine for atodn */ + typedef enum { + SEARCH_OID = 0, + READ_OID = 1, + SEARCH_NAME = 2, + READ_NAME = 3, + UNKNOWN_OID = 4 + } state_t; + + chunk_t oid = chunk_empty; + chunk_t name = chunk_empty; + chunk_t rdns[RDN_MAX]; + int rdn_count = 0; + int dn_len = 0; + int whitespace = 0; + int i = 0; + asn1_t rdn_type; + state_t state = SEARCH_OID; + status_t status = SUCCESS; + + do + { + switch (state) + { + case SEARCH_OID: + if (*src != ' ' && *src != '/' && *src != ',') + { + oid.ptr = src; + oid.len = 1; + state = READ_OID; + } + break; + case READ_OID: + if (*src != ' ' && *src != '=') + { + oid.len++; + } + else + { + for (i = 0; i < X501_RDN_ROOF; i++) + { + if (strlen(x501rdns[i].name) == oid.len + && strncasecmp(x501rdns[i].name, oid.ptr, oid.len) == 0) + { + break; /* found a valid OID */ + } + } + if (i == X501_RDN_ROOF) + { + status = NOT_SUPPORTED; + state = UNKNOWN_OID; + break; + } + /* reset oid and change state */ + oid = chunk_empty; + state = SEARCH_NAME; + } + break; + case SEARCH_NAME: + if (*src != ' ' && *src != '=') + { + name.ptr = src; + name.len = 1; + whitespace = 0; + state = READ_NAME; + } + break; + case READ_NAME: + if (*src != ',' && *src != '/' && *src != '\0') + { + name.len++; + if (*src == ' ') + whitespace++; + else + whitespace = 0; + } + else + { + name.len -= whitespace; + rdn_type = (x501rdns[i].type == ASN1_PRINTABLESTRING + && !is_printablestring(name))? ASN1_T61STRING : x501rdns[i].type; + + if (rdn_count < RDN_MAX) + { + rdns[rdn_count] = + asn1_wrap(ASN1_SET, "m", + asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_wrap(ASN1_OID, "c", x501rdns[i].oid), + asn1_wrap(rdn_type, "c", name) + ) + ); + dn_len += rdns[rdn_count++].len; + } + else + { + status = OUT_OF_RES; + } + /* reset name and change state */ + name = chunk_empty; + state = SEARCH_OID; + } + break; + case UNKNOWN_OID: + break; + } + } while (*src++ != '\0'); + + /* build the distinguished name sequence */ + { + int i; + u_char *pos = build_asn1_object(dn, ASN1_SEQUENCE, dn_len); + + for (i = 0; i < rdn_count; i++) + { + memcpy(pos, rdns[i].ptr, rdns[i].len); + pos += rdns[i].len; + free(rdns[i].ptr); + } + } + + if (status != SUCCESS) + { + free(dn->ptr); + *dn = chunk_empty; + } + return status; +} + +/** + * Implementation of identification_t.get_encoding. + */ +static chunk_t get_encoding(private_identification_t *this) +{ + return this->encoded; +} + +/** + * Implementation of identification_t.get_type. + */ +static id_type_t get_type(private_identification_t *this) +{ + return this->type; +} + +/** + * Implementation of identification_t.contains_wildcards. + */ +static bool contains_wildcards(private_identification_t *this) +{ + switch (this->type) + { + case ID_ANY: + return TRUE; + case ID_FQDN: + case ID_RFC822_ADDR: + return memchr(this->encoded.ptr, '*', this->encoded.len) != NULL; + case ID_DER_ASN1_DN: + /* TODO */ + default: + return FALSE; + + } +} + +/** + * Default implementation of identification_t.equals. + * compares encoded chunk for equality. + */ +static bool equals_binary(private_identification_t *this, private_identification_t *other) +{ + return this->type == other->type && + chunk_equals(this->encoded, other->encoded); +} + +/** + * Special implementation of identification_t.equals for ID_DER_ASN1_DN. + */ +static bool equals_dn(private_identification_t *this, + private_identification_t *other) +{ + return same_dn(this->encoded, other->encoded); +} + +/** + * Default implementation of identification_t.matches. + */ +static bool matches_binary(private_identification_t *this, + private_identification_t *other, int *wildcards) +{ + if (other->type == ID_ANY) + { + if (wildcards) + { + *wildcards = MAX_WILDCARDS; + } + return TRUE; + } + if (wildcards) + { + *wildcards = 0; + } + return this->type == other->type && + chunk_equals(this->encoded, other->encoded); +} + +/** + * Special implementation of identification_t.matches for ID_RFC822_ADDR/ID_FQDN. + * Checks for a wildcard in other-string, and compares it against this-string. + */ +static bool matches_string(private_identification_t *this, + private_identification_t *other, int *wildcards) +{ + u_int len = other->encoded.len; + + if (other->type == ID_ANY) + { + if (wildcards) + { + *wildcards = MAX_WILDCARDS; + } + return TRUE; + } + + if (this->type != other->type) + return FALSE; + + /* try a binary comparison first */ + if (equals_binary(this, other)) + { + if (wildcards) + { + *wildcards = 0; + } + return TRUE; + } + + if (len == 0 || this->encoded.len < len) + return FALSE; + + /* check for single wildcard at the head of the string */ + if (*other->encoded.ptr == '*') + { + if (wildcards) + { + *wildcards = 1; + } + + /* single asterisk matches any string */ + if (len-- == 1) + return TRUE; + + if (memeq(this->encoded.ptr + this->encoded.len - len, other->encoded.ptr + 1, len)) + return TRUE; + } + + return FALSE; +} + +/** + * Special implementation of identification_t.matches for ID_ANY. + * ANY matches only another ANY, but nothing other + */ +static bool matches_any(private_identification_t *this, + private_identification_t *other, int *wildcards) +{ + if (wildcards) + { + *wildcards = 0; + } + return other->type == ID_ANY; +} + +/** + * Special implementation of identification_t.matches for ID_DER_ASN1_DN. + * ANY matches any, even ANY, thats why its there... + */ +static bool matches_dn(private_identification_t *this, + private_identification_t *other, int *wildcards) +{ + if (other->type == ID_ANY) + { + if (wildcards) + { + *wildcards = MAX_WILDCARDS; + } + return TRUE; + } + + if (this->type == other->type) + { + return match_dn(this->encoded, other->encoded, wildcards); + } + return FALSE; +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_identification_t *this = *((private_identification_t**)(args[0])); + char buf[BUF_LEN]; + chunk_t proper, buf_chunk = chunk_from_buf(buf); + int written; + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + switch (this->type) + { + case ID_ANY: + return fprintf(stream, "%%any"); + case ID_IPV4_ADDR: + if (this->encoded.len < sizeof(struct in_addr) || + inet_ntop(AF_INET, this->encoded.ptr, buf, sizeof(buf)) == NULL) + { + return fprintf(stream, "(invalid ID_IPV4_ADDR)"); + } + else + { + return fprintf(stream, "%s", buf); + } + case ID_IPV6_ADDR: + if (this->encoded.len < sizeof(struct in6_addr) || + inet_ntop(AF_INET6, this->encoded.ptr, buf, INET6_ADDRSTRLEN) == NULL) + { + return fprintf(stream, "(invalid ID_IPV6_ADDR)"); + } + else + { + return fprintf(stream, "%s", buf); + } + case ID_FQDN: + { + proper = sanitize_chunk(this->encoded); + written = fprintf(stream, "@%.*s", proper.len, proper.ptr); + chunk_free(&proper); + return written; + } + case ID_RFC822_ADDR: + { + proper = sanitize_chunk(this->encoded); + written = fprintf(stream, "%.*s", proper.len, proper.ptr); + chunk_free(&proper); + return written; + } + case ID_DER_ASN1_DN: + { + snprintf(buf, sizeof(buf), "%.*s", this->encoded.len, this->encoded.ptr); + /* TODO: whats returned on failure?*/ + dntoa(this->encoded, &buf_chunk); + return fprintf(stream, "%s", buf); + } + case ID_DER_ASN1_GN: + return fprintf(stream, "(ASN.1 general Name"); + case ID_KEY_ID: + return fprintf(stream, "(KEY_ID)"); + case ID_DER_ASN1_GN_URI: + { + proper = sanitize_chunk(this->encoded); + written = fprintf(stream, "%.*s", proper.len, proper.ptr); + chunk_free(&proper); + return written; + } + default: + return fprintf(stream, "(unknown ID type: %d)", this->type); + } +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_IDENTIFICATION, print, arginfo_ptr); +} + +/** + * Implementation of identification_t.clone. + */ +static identification_t *clone_(private_identification_t *this) +{ + private_identification_t *clone = identification_create(); + + clone->type = this->type; + clone->encoded = chunk_clone(this->encoded); + clone->public.equals = this->public.equals; + clone->public.matches = this->public.matches; + + return &clone->public; +} + +/** + * Implementation of identification_t.destroy. + */ +static void destroy(private_identification_t *this) +{ + chunk_free(&this->encoded); + free(this); +} + +/** + * Generic constructor used for the other constructors. + */ +static private_identification_t *identification_create(void) +{ + private_identification_t *this = malloc_thing(private_identification_t); + + this->public.get_encoding = (chunk_t (*) (identification_t*))get_encoding; + this->public.get_type = (id_type_t (*) (identification_t*))get_type; + this->public.contains_wildcards = (bool (*) (identification_t *this))contains_wildcards; + this->public.clone = (identification_t* (*) (identification_t*))clone_; + this->public.destroy = (void (*) (identification_t*))destroy; + /* we use these as defaults, the may be overloaded for special ID types */ + this->public.equals = (bool (*) (identification_t*,identification_t*))equals_binary; + this->public.matches = (bool (*) (identification_t*,identification_t*,int*))matches_binary; + + this->encoded = chunk_empty; + + return this; +} + +/* + * Described in header. + */ +identification_t *identification_create_from_string(char *string) +{ + private_identification_t *this = identification_create(); + + if (string == NULL) + { + string = "%any"; + } + if (strchr(string, '=') != NULL) + { + /* we interpret this as an ASCII X.501 ID_DER_ASN1_DN. + * convert from LDAP style or openssl x509 -subject style to ASN.1 DN + */ + if (atodn(string, &this->encoded) != SUCCESS) + { + free(this); + return NULL; + } + this->type = ID_DER_ASN1_DN; + this->public.equals = (bool (*) (identification_t*,identification_t*))equals_dn; + this->public.matches = (bool (*) (identification_t*,identification_t*,int*))matches_dn; + return &this->public; + } + else if (strchr(string, '@') == NULL) + { + if (streq(string, "%any") + || streq(string, "0.0.0.0") + || streq(string, "*") + || streq(string, "::") + || streq(string, "0::0")) + { + /* any ID will be accepted */ + this->type = ID_ANY; + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_any; + return &this->public; + } + else + { + if (strchr(string, ':') == NULL) + { + /* try IPv4 */ + struct in_addr address; + chunk_t chunk = {(void*)&address, sizeof(address)}; + + if (inet_pton(AF_INET, string, &address) <= 0) + { + free(this); + return NULL; + } + this->encoded = chunk_clone(chunk); + this->type = ID_IPV4_ADDR; + return &(this->public); + } + else + { + /* try IPv6 */ + struct in6_addr address; + chunk_t chunk = {(void*)&address, sizeof(address)}; + + if (inet_pton(AF_INET6, string, &address) <= 0) + { + free(this); + return NULL; + } + this->encoded = chunk_clone(chunk); + this->type = ID_IPV6_ADDR; + return &(this->public); + } + } + } + else + { + if (*string == '@') + { + if (*(string + 1) == '#') + { + /* TODO: Pluto handles '#' as hex encoded ID_KEY_ID. */ + free(this); + return NULL; + } + else + { + this->type = ID_FQDN; + this->encoded.ptr = strdup(string + 1); + this->encoded.len = strlen(string + 1); + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_string; + return &(this->public); + } + } + else + { + this->type = ID_RFC822_ADDR; + this->encoded.ptr = strdup(string); + this->encoded.len = strlen(string); + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_string; + return &(this->public); + } + } +} + +/* + * Described in header. + */ +identification_t *identification_create_from_encoding(id_type_t type, chunk_t encoded) +{ + private_identification_t *this = identification_create(); + this->type = type; + switch (type) + { + case ID_ANY: + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_any; + break; + case ID_FQDN: + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_string; + break; + case ID_RFC822_ADDR: + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_string; + break; + case ID_DER_ASN1_DN: + this->public.equals = (bool (*) + (identification_t*,identification_t*))equals_dn; + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_dn; + break; + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + case ID_DER_ASN1_GN: + case ID_KEY_ID: + case ID_DER_ASN1_GN_URI: + default: + break; + } + + /* apply encoded chunk */ + if (type != ID_ANY) + { + this->encoded = chunk_clone(encoded); + } + return &(this->public); +} diff --git a/src/libstrongswan/utils/identification.h b/src/libstrongswan/utils/identification.h new file mode 100644 index 000000000..59c568eaf --- /dev/null +++ b/src/libstrongswan/utils/identification.h @@ -0,0 +1,261 @@ +/** + * @file identification.h + * + * @brief Interface of identification_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + + +#ifndef IDENTIFICATION_H_ +#define IDENTIFICATION_H_ + +typedef enum id_type_t id_type_t; +typedef struct identification_t identification_t; + +#include <library.h> + +#define MAX_WILDCARDS 14 + +/** + * @brief ID Types in a ID payload. + * + * @ingroup utils + */ +enum id_type_t { + + /** + * private type which matches any other id. + */ + ID_ANY = 0, + + /** + * ID data is a single four (4) octet IPv4 address. + */ + ID_IPV4_ADDR = 1, + + /** + * ID data is a fully-qualified domain name string. + * An example of a ID_FQDN is "example.com". + * The string MUST not contain any terminators (e.g., NULL, CR, etc.). + */ + ID_FQDN = 2, + + /** + * ID data is a fully-qualified RFC822 email address string. + * An example of an ID_RFC822_ADDR is "jsmith@example.com". + * The string MUST NOT contain any terminators. + */ + ID_RFC822_ADDR = 3, + + /** + * ID data is an IPv4 subnet (IKEv1 only) + */ + ID_IPV4_ADDR_SUBNET = 4, + + /** + * ID data is a single sixteen (16) octet IPv6 address. + */ + ID_IPV6_ADDR = 5, + + /** + * ID data is an IPv6 subnet (IKEv1 only) + */ + ID_IPV6_ADDR_SUBNET = 6, + + /** + * ID data is an IPv4 address range (IKEv1 only) + */ + ID_IPV4_ADDR_RANGE = 7, + + /** + * ID data is an IPv6 address range (IKEv1 only) + */ + ID_IPV6_ADDR_RANGE = 8, + + /** + * ID data is the binary DER encoding of an ASN.1 X.501 Distinguished Name + */ + ID_DER_ASN1_DN = 9, + + /** + * ID data is the binary DER encoding of an ASN.1 X.509 GeneralName + */ + ID_DER_ASN1_GN = 10, + + /** + * ID data is an opaque octet stream which may be used to pass vendor- + * specific information necessary to do certain proprietary + * types of identification. + */ + ID_KEY_ID = 11, + + /** + * private type which represents a GeneralName of type URI + */ + ID_DER_ASN1_GN_URI = 201, + +}; + +/** + * enum names for id_type_t. + */ +extern enum_name_t *id_type_names; + +/** + * @brief Generic identification, such as used in ID payload. + * + * The following types are possible: + * - ID_IPV4_ADDR + * - ID_FQDN + * - ID_RFC822_ADDR + * - ID_IPV6_ADDR + * - ID_DER_ASN1_DN + * - ID_DER_ASN1_GN + * - ID_KEY_ID + * - ID_DER_ASN1_GN_URI + * + * @b Constructors: + * - identification_create_from_string() + * - identification_create_from_encoding() + * + * @todo Support for ID_DER_ASN1_GN is minimal right now. Comparison + * between them and ID_IPV4_ADDR/RFC822_ADDR would be nice. + * + * @ingroup utils + */ +struct identification_t { + + /** + * @brief Get the encoding of this id, to send over + * the network. + * + * @warning Result points to internal data, do NOT free! + * + * @param this the identification_t object + * @return a chunk containing the encoded bytes + */ + chunk_t (*get_encoding) (identification_t *this); + + /** + * @brief Get the type of this identification. + * + * @param this the identification_t object + * @return id_type_t + */ + id_type_t (*get_type) (identification_t *this); + + /** + * @brief Check if two identification_t objects are equal. + * + * @param this the identification_t object + * @param other other identification_t object + * @return TRUE if the IDs are equal + */ + bool (*equals) (identification_t *this, identification_t *other); + + /** + * @brief Check if an ID matches a wildcard ID. + * + * An identification_t may contain wildcards, such as + * *@strongswan.org. This call checks if a given ID + * (e.g. tester@strongswan.org) belongs to a such wildcard + * ID. Returns TRUE if + * - IDs are identical + * - other is of type ID_ANY + * - other contains a wildcard and matches this + * + * @param this the ID without wildcard + * @param other the ID containing a wildcard + * @param wildcards returns the number of wildcards, may be NULL + * @return TRUE if match is found + */ + bool (*matches) (identification_t *this, identification_t *other, int *wildcards); + + /** + * @brief Check if an ID is a wildcard ID. + * + * If the ID represents multiple IDs (with wildcards, or + * as the type ID_ANY), TRUE is returned. If it is unique, + * FALSE is returned. + * + * @param this identification_t object + * @return TRUE if ID contains wildcards + */ + bool (*contains_wildcards) (identification_t *this); + + /** + * @brief Clone a identification_t instance. + * + * @param this the identification_t object to clone + * @return clone of this + */ + identification_t *(*clone) (identification_t *this); + + /** + * @brief Destroys a identification_t object. + * + * @param this identification_t object + */ + void (*destroy) (identification_t *this); +}; + +/** + * @brief Creates an identification_t object from a string. + * + * @param string input string, which will be converted + * @return + * - created identification_t object, or + * - NULL if unsupported string supplied. + * + * The input string may be e.g. one of the following: + * - ID_IPV4_ADDR: 192.168.0.1 + * - ID_IPV6_ADDR: 2001:0db8:85a3:08d3:1319:8a2e:0370:7345 + * - ID_FQDN: @www.strongswan.org (@indicates FQDN) + * - ID_RFC822_ADDR: alice@wonderland.org + * - ID_DER_ASN1_DN: C=CH, O=Linux strongSwan, CN=bob + * + * In favour of pluto, domainnames are prepended with an @, since + * pluto resolves domainnames without an @ to IPv4 addresses. Since + * we use a seperate host_t class for addresses, this doesn't + * make sense for us. + * + * A distinguished name may contain one or more of the following RDNs: + * ND, UID, DC, CN, S, SN, serialNumber, C, L, ST, O, OU, T, D, + * N, G, I, ID, EN, EmployeeNumber, E, Email, emailAddress, UN, + * unstructuredName, TCGID. + * + * @ingroup utils + */ +identification_t * identification_create_from_string(char *string); + +/** + * @brief Creates an identification_t object from an encoded chunk. + * + * @param type type of this id, such as ID_IPV4_ADDR + * @param encoded encoded bytes, such as from identification_t.get_encoding + * @return identification_t object + * + * In contrast to identification_create_from_string(), this constructor never + * returns NULL, even when the conversion to a string representation fails. + * + * @ingroup utils + */ +identification_t * identification_create_from_encoding(id_type_t type, chunk_t encoded); + +#endif /* IDENTIFICATION_H_ */ diff --git a/src/libstrongswan/utils/iterator.h b/src/libstrongswan/utils/iterator.h new file mode 100644 index 000000000..02a15c534 --- /dev/null +++ b/src/libstrongswan/utils/iterator.h @@ -0,0 +1,166 @@ +/** + * @file iterator.h + * + * @brief Interface iterator_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef ITERATOR_H_ +#define ITERATOR_H_ + +#include <library.h> + +/** + * @brief Iterator hook function prototype. + * + * @param param user supplied parameter + * @param in the value the hook receives from the iterator + * @param out the value supplied as a result to the iterator + * @return TRUE to return "out", FALSE to skip this value + */ +typedef bool (iterator_hook_t)(void *param, void *in, void **out); + + +typedef struct iterator_t iterator_t; + +/** + * @brief Iterator interface, allows iteration over collections. + * + * iterator_t defines an interface for iterating over collections. + * It allows searching, deleting, updating and inserting. + * + * Thanks to JMP for iterator lessons :-) + * + * @b Constructors: + * - via linked_list_t.create_iterator, or + * - any other class which supports the iterator_t interface + * + * @see linked_list_t + * + * @ingroup utils + */ +struct iterator_t { + + /** + * @brief Return number of list items. + * + * @param this calling object + * @return number of list items + */ + int (*get_count) (iterator_t *this); + + /** + * @brief Iterate over all items. + * + * The easy way to iterate over items. + * + * @param this calling object + * @param[out] value item + * @return + * - TRUE, if there was an element available, + * - FALSE otherwise + */ + bool (*iterate) (iterator_t *this, void** value); + + /** + * @brief Hook a function into the iterator. + * + * Sometimes it is useful to hook in an iterator. The hook function is + * called before any successful return of iterate(). It takes the + * iterator value, may manipulate it (or the references object), and returns + * the value that the iterate() function returns. + * A value of NULL deactivates the iterator hook. + * + * @param this calling object + * @param hook iterator hook which manipulates the iterated value + * @param param user supplied parameter to pass back to the hook + */ + void (*set_iterator_hook) (iterator_t *this, iterator_hook_t *hook, + void *param); + + /** + * @brief Inserts a new item before the given iterator position. + * + * The iterator position is not changed after inserting + * + * @param this calling iterator + * @param[in] item value to insert in list + */ + void (*insert_before) (iterator_t *this, void *item); + + /** + * @brief Inserts a new item after the given iterator position. + * + * The iterator position is not changed after inserting. + * + * @param this calling iterator + * @param[in] item value to insert in list + */ + void (*insert_after) (iterator_t *this, void *item); + + /** + * @brief Replace the current item at current iterator position. + * + * The iterator position is not changed after replacing. + * + * @param this calling iterator + * @param[out] old_item old value will be written here(can be NULL) + * @param[in] new_item new value + * + * @return + * - SUCCESS + * - FAILED if iterator is on an invalid position + */ + status_t (*replace) (iterator_t *this, void **old_item, void *new_item); + + /** + * @brief Removes an element from list at the given iterator position. + * + * The iterator is set the the following position: + * - to the item before, if available + * - it gets reseted, otherwise + * + * @param this calling object + * @return + * - SUCCESS + * - FAILED if iterator is on an invalid position + */ + status_t (*remove) (iterator_t *this); + + /** + * @brief Resets the iterator position. + * + * After reset, the iterator_t objects doesn't point to an element. + * A call to iterator_t.has_next is necessary to do any other operations + * with the resetted iterator. + * + * @param this calling object + */ + void (*reset) (iterator_t *this); + + /** + * @brief Destroys an iterator. + * + * @param this iterator to destroy + * + */ + void (*destroy) (iterator_t *this); +}; + +#endif /*ITERATOR_H_*/ diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c new file mode 100644 index 000000000..b8a023270 --- /dev/null +++ b/src/libstrongswan/utils/leak_detective.c @@ -0,0 +1,459 @@ +/** + * @file leak_detective.c + * + * @brief Allocation hooks to find memory leaks. + */ + +/* + * Copyright (C) 2006 Martin Willi + * 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. + */ + +#include <stddef.h> +#include <string.h> +#include <stdio.h> +#include <malloc.h> +#include <signal.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <dlfcn.h> +#include <unistd.h> +#include <syslog.h> +#include <pthread.h> +#include <netdb.h> +#include <printf.h> +#ifdef HAVE_BACKTRACE +# include <execinfo.h> +#endif /* HAVE_BACKTRACE */ + +#include "leak_detective.h" + +#include <library.h> +#include <debug.h> + +#ifdef LEAK_DETECTIVE + +/** + * Magic value which helps to detect memory corruption. Yummy! + */ +#define MEMORY_HEADER_MAGIC 0x7ac0be11 + +/** + * Pattern which is filled in memory before freeing it + */ +#define MEMORY_FREE_PATTERN 0xFF + +/** + * Pattern which is filled in newly allocated memory + */ +#define MEMORY_ALLOC_PATTERN 0xEE + + +static void install_hooks(void); +static void uninstall_hooks(void); +static void *malloc_hook(size_t, const void *); +static void *realloc_hook(void *, size_t, const void *); +static void free_hook(void*, const void *); + +static u_int count_malloc = 0; +static u_int count_free = 0; +static u_int count_realloc = 0; + +typedef struct memory_header_t memory_header_t; + +/** + * Header which is prepended to each allocated memory block + */ +struct memory_header_t { + /** + * Magci byte which must(!) hold MEMORY_HEADER_MAGIC + */ + u_int32_t magic; + + /** + * Number of bytes following after the header + */ + size_t bytes; + + /** + * Stack frames at the time of allocation + */ + void *stack_frames[STACK_FRAMES_COUNT]; + + /** + * Number of stacks frames obtained in stack_frames + */ + int stack_frame_count; + + /** + * Pointer to previous entry in linked list + */ + memory_header_t *previous; + + /** + * Pointer to next entry in linked list + */ + memory_header_t *next; +}; + +/** + * first mem header is just a dummy to chain + * the others on it... + */ +static memory_header_t first_header = { + magic: MEMORY_HEADER_MAGIC, + bytes: 0, + stack_frame_count: 0, + previous: NULL, + next: NULL +}; + +/** + * standard hooks, used to temparily remove hooking + */ +static void *old_malloc_hook, *old_realloc_hook, *old_free_hook; + +/** + * are the hooks currently installed? + */ +static bool installed = FALSE; + +/** + * Mutex to exclusivly uninstall hooks, access heap list + */ +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + +/** + * log stack frames queried by backtrace() + * TODO: Dump symbols of static functions. This could be done with + * the addr2line utility or the GNU BFD Library... + */ +static void log_stack_frames(void **stack_frames, int stack_frame_count) +{ +#ifdef HAVE_BACKTRACE + char **strings; + size_t i; + + strings = backtrace_symbols (stack_frames, stack_frame_count); + + DBG1(" dumping %d stack frame addresses", stack_frame_count); + + for (i = 0; i < stack_frame_count; i++) + { + DBG1(" %s", strings[i]); + } + free (strings); +#endif /* HAVE_BACKTRACE */ +} + +/** + * Whitelist, which contains address ranges in stack frames ignored when leaking. + * + * This is necessary, as some function use allocation hacks (static buffers) + * and so on, which we want to suppress on leak reports. + * + * The range_size is calculated using the readelf utility, e.g.: + * readelf -s /lib/glibc.so.6 + * The values are for glibc-2.4 and may or may not be correct on other systems. + */ +typedef struct whitelist_t whitelist_t; + +struct whitelist_t { + void* range_start; + size_t range_size; +}; + +#ifdef LIBCURL +/* dummy declaration for whitelisting */ +void *Curl_getaddrinfo(void); +#endif /* LIBCURL */ + +whitelist_t whitelist[] = { + {pthread_create, 2542}, + {pthread_setspecific, 217}, + {mktime, 60}, + {tzset, 123}, + {inet_ntoa, 249}, + {strerror, 180}, + {getprotobynumber, 291}, + {getservbyport, 311}, + {register_printf_function, 159}, + {syslog, 45}, + {dlopen, 109}, +# ifdef LIBCURL + /* from /usr/lib/libcurl.so.3 */ + {Curl_getaddrinfo, 480}, +# endif /* LIBCURL */ +}; + +/** + * Check if this stack frame is whitelisted. + */ +static bool is_whitelisted(void **stack_frames, int stack_frame_count) +{ + int i, j; + + for (i=0; i< stack_frame_count; i++) + { + for (j=0; j<sizeof(whitelist)/sizeof(whitelist_t); j++) + { + if (stack_frames[i] >= whitelist[j].range_start && + stack_frames[i] <= (whitelist[j].range_start + whitelist[j].range_size)) + { + return TRUE; + } + } + } + return FALSE; +} + +/** + * Report leaks at library destruction + */ +void report_leaks() +{ + memory_header_t *hdr; + int leaks = 0; + + for (hdr = first_header.next; hdr != NULL; hdr = hdr->next) + { + if (!is_whitelisted(hdr->stack_frames, hdr->stack_frame_count)) + { + DBG1("Leak (%d bytes at %p):", hdr->bytes, hdr + 1); + log_stack_frames(hdr->stack_frames, hdr->stack_frame_count); + leaks++; + } + } + + switch (leaks) + { + case 0: + DBG1("No leaks detected"); + break; + case 1: + DBG1("One leak detected"); + break; + default: + DBG1("%d leaks detected", leaks); + break; + } +} + +/** + * Installs the malloc hooks, enables leak detection + */ +static void install_hooks() +{ + if (!installed) + { + old_malloc_hook = __malloc_hook; + old_realloc_hook = __realloc_hook; + old_free_hook = __free_hook; + __malloc_hook = malloc_hook; + __realloc_hook = realloc_hook; + __free_hook = free_hook; + installed = TRUE; + } +} + +/** + * Uninstalls the malloc hooks, disables leak detection + */ +static void uninstall_hooks() +{ + if (installed) + { + __malloc_hook = old_malloc_hook; + __free_hook = old_free_hook; + __realloc_hook = old_realloc_hook; + installed = FALSE; + } +} + +/** + * Hook function for malloc() + */ +void *malloc_hook(size_t bytes, const void *caller) +{ + memory_header_t *hdr; + + pthread_mutex_lock(&mutex); + count_malloc++; + uninstall_hooks(); + hdr = malloc(bytes + sizeof(memory_header_t)); + /* set to something which causes crashes */ + memset(hdr, MEMORY_ALLOC_PATTERN, bytes + sizeof(memory_header_t)); + + hdr->magic = MEMORY_HEADER_MAGIC; + hdr->bytes = bytes; + hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT); + install_hooks(); + + /* insert at the beginning of the list */ + hdr->next = first_header.next; + if (hdr->next) + { + hdr->next->previous = hdr; + } + hdr->previous = &first_header; + first_header.next = hdr; + pthread_mutex_unlock(&mutex); + return hdr + 1; +} + +/** + * Hook function for free() + */ +void free_hook(void *ptr, const void *caller) +{ + void *stack_frames[STACK_FRAMES_COUNT]; + int stack_frame_count; + memory_header_t *hdr = ptr - sizeof(memory_header_t); + + /* allow freeing of NULL */ + if (ptr == NULL) + { + return; + } + + pthread_mutex_lock(&mutex); + count_free++; + uninstall_hooks(); + if (hdr->magic != MEMORY_HEADER_MAGIC) + { + DBG1("freeing of invalid memory (%p, MAGIC 0x%x != 0x%x):", + ptr, hdr->magic, MEMORY_HEADER_MAGIC); + stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT); + log_stack_frames(stack_frames, stack_frame_count); + install_hooks(); + pthread_mutex_unlock(&mutex); + return; + } + + /* remove item from list */ + if (hdr->next) + { + hdr->next->previous = hdr->previous; + } + hdr->previous->next = hdr->next; + + /* clear MAGIC, set mem to something remarkable */ + memset(hdr, MEMORY_FREE_PATTERN, hdr->bytes + sizeof(memory_header_t)); + + free(hdr); + install_hooks(); + pthread_mutex_unlock(&mutex); +} + +/** + * Hook function for realloc() + */ +void *realloc_hook(void *old, size_t bytes, const void *caller) +{ + memory_header_t *hdr; + void *stack_frames[STACK_FRAMES_COUNT]; + int stack_frame_count; + + /* allow reallocation of NULL */ + if (old == NULL) + { + return malloc_hook(bytes, caller); + } + + hdr = old - sizeof(memory_header_t); + + pthread_mutex_lock(&mutex); + count_realloc++; + uninstall_hooks(); + if (hdr->magic != MEMORY_HEADER_MAGIC) + { + DBG1("reallocation of invalid memory (%p):", old); + stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT); + log_stack_frames(stack_frames, stack_frame_count); + install_hooks(); + pthread_mutex_unlock(&mutex); + raise(SIGKILL); + return NULL; + } + + hdr = realloc(hdr, bytes + sizeof(memory_header_t)); + + /* update statistics */ + hdr->bytes = bytes; + hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT); + + /* update header of linked list neighbours */ + if (hdr->next) + { + hdr->next->previous = hdr; + } + hdr->previous->next = hdr; + install_hooks(); + pthread_mutex_unlock(&mutex); + return hdr + 1; +} + +/** + * Setup leak detective + */ +void __attribute__ ((constructor)) leak_detective_init() +{ + install_hooks(); +} + +/** + * Clean up leak detective + */ +void __attribute__ ((destructor)) leak_detective_cleanup() +{ + uninstall_hooks(); + report_leaks(); +} + +/** + * Log memory allocation statistics + */ +void leak_detective_status(FILE *stream) +{ + u_int blocks = 0; + size_t bytes = 0; + memory_header_t *hdr = &first_header; + + pthread_mutex_lock(&mutex); + while ((hdr = hdr->next)) + { + blocks++; + bytes += hdr->bytes; + } + pthread_mutex_unlock(&mutex); + + fprintf(stream, "allocation statistics:\n"); + fprintf(stream, " call stats: malloc: %d, free: %d, realloc: %d\n", + count_malloc, count_free, count_realloc); + fprintf(stream, " allocated %d blocks, total size %d bytes (avg. %d bytes)\n", + blocks, bytes, bytes/blocks); +} + +#else /* !LEAK_DETECTION */ + +/** + * Dummy when !using LEAK_DETECTIVE + */ +void leak_detective_status(FILE *stream) +{ + +} + +#endif /* LEAK_DETECTION */ diff --git a/src/libstrongswan/utils/leak_detective.h b/src/libstrongswan/utils/leak_detective.h new file mode 100644 index 000000000..d4016b06e --- /dev/null +++ b/src/libstrongswan/utils/leak_detective.h @@ -0,0 +1,35 @@ +/** + * @file leak_detective.h + * + * @brief malloc/free hooks to detect leaks. + */ + +/* + * Copyright (C) 2006 Martin Willi + * 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. + */ + +#ifndef LEAK_DETECTIVE_H_ +#define LEAK_DETECTIVE_H_ + +/** + * Log status information about allocation + */ +void leak_detective_status(FILE *stream); + +/** + * Max number of stack frames to include in a backtrace. + */ +#define STACK_FRAMES_COUNT 30 + +#endif /* LEAK_DETECTIVE_H_ */ diff --git a/src/libstrongswan/utils/lexparser.c b/src/libstrongswan/utils/lexparser.c new file mode 100644 index 000000000..9d3f06593 --- /dev/null +++ b/src/libstrongswan/utils/lexparser.c @@ -0,0 +1,137 @@ +/** + * @file lexparser.c + * + * @brief lexical parser for text-based configuration files + * + */ + +/* + * Copyright (C) 2001-2006 Andreas Steffen, Zuercher Hochschule Winterthur + * + * 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. + */ + +#include <string.h> + +#include "lexparser.h" + + +/** + * eat whitespace + */ +bool eat_whitespace(chunk_t *src) +{ + while (src->len > 0 && (*src->ptr == ' ' || *src->ptr == '\t')) + { + src->ptr++; src->len--; + } + return src->len > 0 && *src->ptr != '#'; +} + +/** + * compare string with chunk + */ +bool match(const char *pattern, const chunk_t *ch) +{ + return ch->len == strlen(pattern) && strncmp(pattern, ch->ptr, ch->len) == 0; +} + +/** + * extracts a token ending with a given termination symbol + */ +bool extract_token(chunk_t *token, const char termination, chunk_t *src) +{ + u_char *eot = memchr(src->ptr, termination, src->len); + + /* initialize empty token */ + *token = chunk_empty; + + if (eot == NULL) /* termination symbol not found */ + { + return FALSE; + } + + /* extract token */ + token->ptr = src->ptr; + token->len = (u_int)(eot - src->ptr); + + /* advance src pointer after termination symbol */ + src->ptr = eot + 1; + src->len -= (token->len + 1); + + return TRUE; +} + +/** + * fetches a new line terminated by \n or \r\n + */ +bool fetchline(chunk_t *src, chunk_t *line) +{ + if (src->len == 0) /* end of src reached */ + return FALSE; + + if (extract_token(line, '\n', src)) + { + if (line->len > 0 && *(line->ptr + line->len -1) == '\r') + line->len--; /* remove optional \r */ + } + else /*last line ends without newline */ + { + *line = *src; + src->ptr += src->len; + src->len = 0; + } + return TRUE; +} + +err_t extract_value(chunk_t *value, chunk_t *line) +{ + char delimiter = ' '; + + if (!eat_whitespace(line)) + { + *value = chunk_empty; + return NULL; + } + if (*line->ptr == '\'' || *line->ptr == '"') + { + delimiter = *line->ptr; + line->ptr++; line->len--; + } + if (!extract_token(value, delimiter, line)) + { + if (delimiter == ' ') + { + *value = *line; + line->len = 0; + } + else + { + return "missing second delimiter"; + } + } + return NULL; +} + +/** + * extracts a parameter: value pair + */ +err_t extract_parameter_value(chunk_t *name, chunk_t *value, chunk_t *line) +{ + /* extract name */ + if (!extract_token(name,':', line)) + { + return "missing ':'"; + } + + /* extract value */ + return extract_value(value, line); +} diff --git a/src/libstrongswan/utils/lexparser.h b/src/libstrongswan/utils/lexparser.h new file mode 100644 index 000000000..e3c2c4c70 --- /dev/null +++ b/src/libstrongswan/utils/lexparser.h @@ -0,0 +1,57 @@ +/** + * @file lexparser.h + * + * @brief lexical parser for text-based configuration files + * + */ + +/* + * Copyright (C) 2001-2006 Andreas Steffen, Zuercher Hochschule Winterthur + * + * 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. + */ + +#include <library.h> + +/** + * @brief Eats whitespace + */ +bool eat_whitespace(chunk_t *src); + +/** + * @brief Compare null-terminated pattern with chunk + */ +bool match(const char *pattern, const chunk_t *ch); + +/** + * @brief Extracts a token ending with a given termination symbol + */ +bool extract_token(chunk_t *token, const char termination, chunk_t *src); + +/** + * @brief Fetches a new text line terminated by \n or \r\n + */ +bool fetchline(chunk_t *src, chunk_t *line); + +/** + * @brief Extracts a value that might be single or double quoted + */ +err_t extract_value(chunk_t *value, chunk_t *line); + +/** + * @brief extracts a name: value pair from a text line + */ +err_t extract_name_value(chunk_t *name, chunk_t *value, chunk_t *line); + +/** + * @brief extracts a parameter: value from a text line + */ +err_t extract_parameter_value(chunk_t *name, chunk_t *value, chunk_t *line); diff --git a/src/libstrongswan/utils/linked_list.c b/src/libstrongswan/utils/linked_list.c new file mode 100644 index 000000000..de043a02e --- /dev/null +++ b/src/libstrongswan/utils/linked_list.c @@ -0,0 +1,763 @@ +/** + * @file linked_list.c + * + * @brief Implementation of linked_list_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#include <stdlib.h> + +#include "linked_list.h" + +typedef struct element_t element_t; + +/** + * This element holds a pointer to the value it represents. + */ +struct element_t { + + /** + * Value of a list item. + */ + void *value; + + /** + * Previous list element. + * + * NULL if first element in list. + */ + element_t *previous; + + /** + * Next list element. + * + * NULL if last element in list. + */ + element_t *next; +}; + +/** + * Creates an empty linked list object. + */ +element_t *element_create(void *value) +{ + element_t *this = malloc_thing(element_t); + + this->previous = NULL; + this->next = NULL; + this->value = value; + + return (this); +} + + +typedef struct private_linked_list_t private_linked_list_t; + +/** + * Private data of a linked_list_t object. + * + */ +struct private_linked_list_t { + /** + * Public part of linked list. + */ + linked_list_t public; + + /** + * Number of items in the list. + */ + int count; + + /** + * First element in list. + * NULL if no elements in list. + */ + element_t *first; + + /** + * Last element in list. + * NULL if no elements in list. + */ + element_t *last; +}; + + +typedef struct private_iterator_t private_iterator_t; + +/** + * Private variables and functions of linked list iterator. + */ +struct private_iterator_t { + /** + * Public part of linked list iterator. + */ + iterator_t public; + + /** + * Associated linked list. + */ + private_linked_list_t * list; + + /** + * Current element of the iterator. + */ + element_t *current; + + /** + * Direction of iterator. + */ + bool forward; + + /** + * Mutex to use to synchronize access + */ + pthread_mutex_t *mutex; + + /** + * iteration hook + */ + iterator_hook_t *hook; + + /** + * user parameter for iterator hook + */ + void *hook_param; +}; + +/** + * Implementation of iterator_t.get_count. + */ +static int get_list_count(private_iterator_t *this) +{ + return this->list->count; +} + +/** + * default iterator hook which does nothing + */ +static bool iterator_hook(void *param, void *in, void **out) +{ + *out = in; + return TRUE; +} + +/** + * Implementation of iterator_t.set_iterator_hook. + */ +static void set_iterator_hook(private_iterator_t *this, iterator_hook_t *hook, + void* param) +{ + if (hook == NULL) + { + this->hook = iterator_hook; + this->hook_param = NULL; + } + else + { + this->hook = hook; + this->hook_param = param; + } +} + +/** + * Implementation of iterator_t.iterate. + */ +static bool iterate(private_iterator_t *this, void** value) +{ + if (this->list->count == 0) + { + return FALSE; + } + if (this->current == NULL) + { + this->current = (this->forward) ? this->list->first : this->list->last; + if (!this->hook(this->hook_param, this->current->value, value)) + { + return iterate(this, value); + } + return TRUE; + } + if (this->forward) + { + if (this->current->next == NULL) + { + return FALSE; + } + this->current = this->current->next; + if (!this->hook(this->hook_param, this->current->value, value)) + { + return iterate(this, value); + } + return TRUE; + } + if (this->current->previous == NULL) + { + return FALSE; + } + this->current = this->current->previous; + if (!this->hook(this->hook_param, this->current->value, value)) + { + return iterate(this, value); + } + return TRUE; +} + +/** + * Implementation of iterator_t.reset. + */ +static void iterator_reset(private_iterator_t *this) +{ + this->current = NULL; +} + +/** + * Implementation of iterator_t.remove. + */ +static status_t remove_(private_iterator_t *this) +{ + element_t *new_current; + + if (this->current == NULL) + { + return NOT_FOUND; + } + + if (this->list->count == 0) + { + return NOT_FOUND; + } + /* find out the new iterator position, depending on iterator direction */ + if (this->forward && this->current->previous != NULL) + { + new_current = this->current->previous; + } + else if (!this->forward && this->current->next != NULL) + { + new_current = this->current->next; + } + else + { + new_current = NULL; + } + + /* now delete the entry :-) */ + if (this->current->previous == NULL) + { + if (this->current->next == NULL) + { + this->list->first = NULL; + this->list->last = NULL; + } + else + { + this->current->next->previous = NULL; + this->list->first = this->current->next; + } + } + else if (this->current->next == NULL) + { + this->current->previous->next = NULL; + this->list->last = this->current->previous; + } + else + { + this->current->previous->next = this->current->next; + this->current->next->previous = this->current->previous; + } + + this->list->count--; + free(this->current); + /* set the new iterator position */ + this->current = new_current; + return SUCCESS; +} + +/** + * Implementation of iterator_t.insert_before. + */ +static void insert_before(private_iterator_t * iterator, void *item) +{ + if (iterator->current == NULL) + { + iterator->list->public.insert_first(&(iterator->list->public), item); + } + + element_t *element = element_create(item); + if (iterator->current->previous == NULL) + { + iterator->current->previous = element; + element->next = iterator->current; + iterator->list->first = element; + } + else + { + iterator->current->previous->next = element; + element->previous = iterator->current->previous; + iterator->current->previous = element; + element->next = iterator->current; + } + iterator->list->count++; +} + +/** + * Implementation of iterator_t.replace. + */ +static status_t replace(private_iterator_t *this, void **old_item, void *new_item) +{ + if (this->current == NULL) + { + return NOT_FOUND; + } + if (old_item != NULL) + { + *old_item = this->current->value; + } + this->current->value = new_item; + + return SUCCESS; +} + +/** + * Implementation of iterator_t.insert_after. + */ +static void insert_after(private_iterator_t *iterator, void *item) +{ + if (iterator->current == NULL) + { + iterator->list->public.insert_first(&(iterator->list->public),item); + return; + } + + element_t *element = element_create(item); + if (iterator->current->next == NULL) + { + iterator->current->next = element; + element->previous = iterator->current; + iterator->list->last = element; + } + else + { + iterator->current->next->previous = element; + element->next = iterator->current->next; + iterator->current->next = element; + element->previous = iterator->current; + } + iterator->list->count++; +} + +/** + * Implementation of iterator_t.destroy. + */ +static void iterator_destroy(private_iterator_t *this) +{ + if (this->mutex) + { + pthread_mutex_unlock(this->mutex); + } + free(this); +} + +/** + * Implementation of linked_list_t.get_count. + */ +static int get_count(private_linked_list_t *this) +{ + return this->count; +} + +/** + * Implementation of linked_list_t.insert_first. + */ +static void insert_first(private_linked_list_t *this, void *item) +{ + element_t *element; + + element = element_create(item); + if (this->count == 0) + { + /* first entry in list */ + this->first = element; + this->last = element; + element->previous = NULL; + element->next = NULL; + } + else + { + element_t *old_first_element = this->first; + element->next = old_first_element; + element->previous = NULL; + old_first_element->previous = element; + this->first = element; + } + this->count++; +} + +/** + * Implementation of linked_list_t.remove_first. + */ +static status_t remove_first(private_linked_list_t *this, void **item) +{ + element_t *element = this->first; + + if (element == NULL) + { + return NOT_FOUND; + } + if (element->next != NULL) + { + element->next->previous = NULL; + } + this->first = element->next; + + if (item != NULL) + { + *item = element->value; + } + if (--this->count == 0) + { + this->last = NULL; + } + + free(element); + + return SUCCESS; +} + +/** + * Implementation of linked_list_t.get_first. + */ +static status_t get_first(private_linked_list_t *this, void **item) +{ + if (this->count == 0) + { + return NOT_FOUND; + } + *item = this->first->value; + return SUCCESS; +} + +/** + * Implementation of linked_list_t.insert_last. + */ +static void insert_last(private_linked_list_t *this, void *item) +{ + element_t *element = element_create(item); + + if (this->count == 0) + { + /* first entry in list */ + this->first = element; + this->last = element; + element->previous = NULL; + element->next = NULL; + } + else + { + element_t *old_last_element = this->last; + element->previous = old_last_element; + element->next = NULL; + old_last_element->next = element; + this->last = element; + } + this->count++; +} + +/** + * Implementation of linked_list_t.remove_last. + */ +static status_t remove_last(private_linked_list_t *this, void **item) +{ + element_t *element = this->last; + + if (element == NULL) + { + return NOT_FOUND; + } + if (element->previous != NULL) + { + element->previous->next = NULL; + } + this->last = element->previous; + + if (item != NULL) + { + *item = element->value; + } + if (--this->count == 0) + { + this->first = NULL; + } + + free(element); + + return SUCCESS; +} + +/** + * Implementation of linked_list_t.insert_at_position. + */ +static status_t insert_at_position (private_linked_list_t *this,size_t position, void *item) +{ + element_t *current_element; + int i; + + if (this->count <= position) + { + return INVALID_ARG; + } + + current_element = this->first; + + for (i = 0; i < position;i++) + { + current_element = current_element->next; + } + + if (current_element == NULL) + { + this->public.insert_last(&(this->public),item); + return SUCCESS; + } + + element_t *element = element_create(item); + if (current_element->previous == NULL) + { + current_element->previous = element; + element->next = current_element; + this->first = element; + } + else + { + current_element->previous->next = element; + element->previous = current_element->previous; + current_element->previous = element; + element->next = current_element; + } + + + this->count++; + return SUCCESS; +} + +/** + * Implementation of linked_list_t.remove_at_position. + */ +static status_t remove_at_position(private_linked_list_t *this,size_t position, void **item) +{ + iterator_t *iterator; + int i; + + if (this->count <= position) + { + return INVALID_ARG; + } + + iterator = this->public.create_iterator(&(this->public),TRUE); + iterator->iterate(iterator, item); + for (i = 0; i < position; i++) + { + if (!iterator->iterate(iterator, item)) + { + iterator->destroy(iterator); + return INVALID_ARG; + } + } + iterator->remove(iterator); + iterator->destroy(iterator); + + return SUCCESS; +} + +/** + * Implementation of linked_list_t.get_at_position. + */ +static status_t get_at_position(private_linked_list_t *this,size_t position, void **item) +{ + int i; + iterator_t *iterator; + + if (this->count <= position) + { + return INVALID_ARG; + } + + iterator = this->public.create_iterator(&(this->public),TRUE); + iterator->iterate(iterator, item); + for (i = 0; i < position; i++) + { + if (!iterator->iterate(iterator, item)) + { + iterator->destroy(iterator); + return INVALID_ARG; + } + } + iterator->destroy(iterator); + return SUCCESS; +} + +/** + * Implementation of linked_list_t.get_last. + */ +static status_t get_last(private_linked_list_t *this, void **item) +{ + if (this->count == 0) + { + return NOT_FOUND; + } + + *item = this->last->value; + + return SUCCESS; +} + +/** + * Implementation of linked_list_t.invoke. + */ +static void invoke(private_linked_list_t *this, size_t offset) +{ + element_t *current = this->first; + + while (current) + { + void (**method)(void*) = current->value + offset; + (*method)(current->value); + current = current->next; + } +} + +/** + * Implementation of linked_list_t.destroy. + */ +static void destroy(private_linked_list_t *this) +{ + void *value; + /* Remove all list items before destroying list */ + while (this->public.remove_first(&(this->public), &value) == SUCCESS) + { + /* values are not destroyed so memory leaks are possible + * if list is not empty when deleting */ + } + free(this); +} + +/** + * Implementation of linked_list_t.destroy_offset. + */ +static void destroy_offset(private_linked_list_t *this, size_t offset) +{ + element_t *current = this->first, *next; + + while (current) + { + void (**method)(void*) = current->value + offset; + (*method)(current->value); + next = current->next; + free(current); + current = next; + } + free(this); +} + +/** + * Implementation of linked_list_t.destroy_function. + */ +static void destroy_function(private_linked_list_t *this, void (*fn)(void*)) +{ + element_t *current = this->first, *next; + + while (current) + { + fn(current->value); + next = current->next; + free(current); + current = next; + } + free(this); +} + +/** + * Implementation of linked_list_t.create_iterator. + */ +static iterator_t *create_iterator(private_linked_list_t *linked_list, bool forward) +{ + private_iterator_t *this = malloc_thing(private_iterator_t); + + this->public.get_count = (int (*) (iterator_t*)) get_list_count; + this->public.iterate = (bool (*) (iterator_t*, void **value)) iterate; + this->public.set_iterator_hook = (void(*)(iterator_t*, iterator_hook_t*, void*))set_iterator_hook; + this->public.insert_before = (void (*) (iterator_t*, void *item)) insert_before; + this->public.insert_after = (void (*) (iterator_t*, void *item)) insert_after; + this->public.replace = (status_t (*) (iterator_t*, void **, void *)) replace; + this->public.remove = (status_t (*) (iterator_t*)) remove_; + this->public.reset = (void (*) (iterator_t*)) iterator_reset; + this->public.destroy = (void (*) (iterator_t*)) iterator_destroy; + + this->forward = forward; + this->current = NULL; + this->list = linked_list; + this->mutex = NULL; + this->hook = iterator_hook; + + return &this->public; +} + +/** + * Implementation of linked_list_t.create_iterator_locked. + */ +static iterator_t *create_iterator_locked(private_linked_list_t *linked_list, + pthread_mutex_t *mutex) +{ + private_iterator_t *this = (private_iterator_t*)create_iterator(linked_list, TRUE); + this->mutex = mutex; + + pthread_mutex_lock(mutex); + + return &this->public; +} + +/* + * Described in header. + */ +linked_list_t *linked_list_create() +{ + private_linked_list_t *this = malloc_thing(private_linked_list_t); + + this->public.get_count = (int (*) (linked_list_t *)) get_count; + this->public.create_iterator = (iterator_t * (*) (linked_list_t *,bool))create_iterator; + this->public.create_iterator_locked = (iterator_t * (*) (linked_list_t *,pthread_mutex_t*))create_iterator_locked; + this->public.get_first = (status_t (*) (linked_list_t *, void **item))get_first; + this->public.get_last = (status_t (*) (linked_list_t *, void **item))get_last; + this->public.insert_first = (void (*) (linked_list_t *, void *item))insert_first; + this->public.insert_last = (void (*) (linked_list_t *, void *item))insert_last; + this->public.remove_first = (status_t (*) (linked_list_t *, void **item))remove_first; + this->public.remove_last = (status_t (*) (linked_list_t *, void **item))remove_last; + this->public.insert_at_position = (status_t (*) (linked_list_t *,size_t, void *))insert_at_position; + this->public.remove_at_position = (status_t (*) (linked_list_t *,size_t, void **))remove_at_position; + this->public.get_at_position = (status_t (*) (linked_list_t *,size_t, void **))get_at_position; + this->public.invoke = (void (*)(linked_list_t*,size_t))invoke; + this->public.destroy = (void (*) (linked_list_t *))destroy; + this->public.destroy_offset = (void (*) (linked_list_t *,size_t))destroy_offset; + this->public.destroy_function = (void (*)(linked_list_t*,void(*)(void*)))destroy_function; + + this->count = 0; + this->first = NULL; + this->last = NULL; + + return &this->public; +} diff --git a/src/libstrongswan/utils/linked_list.h b/src/libstrongswan/utils/linked_list.h new file mode 100644 index 000000000..58bcbbdaa --- /dev/null +++ b/src/libstrongswan/utils/linked_list.h @@ -0,0 +1,232 @@ +/** + * @file linked_list.h + * + * @brief Interface of linked_list_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef LINKED_LIST_H_ +#define LINKED_LIST_H_ + +typedef struct linked_list_t linked_list_t; + +#include <pthread.h> + +#include <library.h> +#include <utils/iterator.h> + +/** + * @brief Class implementing a double linked list. + * + * General purpose linked list. This list is not synchronized. + * + * @b Costructors: + * - linked_list_create() + * + * @ingroup utils + */ +struct linked_list_t { + + /** + * @brief Gets the count of items in the list. + * + * @param this calling object + * @return number of items in list + */ + int (*get_count) (linked_list_t *this); + + /** + * @brief Creates a iterator for the given list. + * + * @warning Created iterator_t object has to get destroyed by the caller. + * + * @param this calling object + * @param forward iterator direction (TRUE: front to end) + * @return new iterator_t object + */ + iterator_t *(*create_iterator) (linked_list_t *this, bool forward); + + /** + * @brief Creates a iterator, locking a mutex. + * + * The supplied mutex is acquired immediately, and released + * when the iterator gets destroyed. + * + * @param this calling object + * @param mutex mutex to use for exclusive access + * @return new iterator_t object + */ + iterator_t *(*create_iterator_locked) (linked_list_t *this, + pthread_mutex_t *mutex); + + /** + * @brief Inserts a new item at the beginning of the list. + * + * @param this calling object + * @param[in] item item value to insert in list + */ + void (*insert_first) (linked_list_t *this, void *item); + + /** + * @brief Removes the first item in the list and returns its value. + * + * @param this calling object + * @param[out] item returned value of first item, or NULL + * @return + * - SUCCESS + * - NOT_FOUND, if list is empty + */ + status_t (*remove_first) (linked_list_t *this, void **item); + + /** + * @brief Returns the value of the first list item without removing it. + * + * @param this calling object + * @param[out] item returned value of first item + * @return + * - SUCCESS + * - NOT_FOUND, if list is empty + */ + status_t (*get_first) (linked_list_t *this, void **item); + + /** + * @brief Inserts a new item at the end of the list. + * + * @param this calling object + * @param[in] item value to insert into list + */ + void (*insert_last) (linked_list_t *this, void *item); + + /** + * @brief Inserts a new item at a given position in the list. + * + * @param this calling object + * @param position position starting at 0 to insert new entry + * @param[in] item value to insert into list + * @return + * - SUCCESS + * - INVALID_ARG if position not existing + */ + status_t (*insert_at_position) (linked_list_t *this,size_t position, void *item); + + /** + * @brief Removes an item from a given position in the list. + * + * @param this calling object + * @param position position starting at 0 to remove entry from + * @param[out] item removed item will be stored at this location + * @return + * - SUCCESS + * - INVALID_ARG if position not existing + */ + status_t (*remove_at_position) (linked_list_t *this, size_t position, void **item); + + /** + * @brief Get an item from a given position in the list. + * + * @param this calling object + * @param position position starting at 0 to get entry from + * @param[out] item item will be stored at this location + * @return + * - SUCCESS + * - INVALID_ARG if position not existing + */ + status_t (*get_at_position) (linked_list_t *this, size_t position, void **item); + + /** + * @brief Removes the last item in the list and returns its value. + * + * @param this calling object + * @param[out] item returned value of last item, or NULL + * @return + * - SUCCESS + * - NOT_FOUND if list is empty + */ + status_t (*remove_last) (linked_list_t *this, void **item); + + /** + * @brief Returns the value of the last list item without removing it. + * + * @param this calling object + * @param[out] item returned value of last item + * @return + * - SUCCESS + * - NOT_FOUND if list is empty + */ + status_t (*get_last) (linked_list_t *this, void **item); + + /** + * @brief Invoke a method on all of the contained objects. + * + * If a linked list contains objects with function pointers, + * invoke() can call a method on each of the objects. The + * method is specified by an offset of the function pointer, + * which can be evalutated at compile time using the offsetof + * macro, e.g.: list->invoke(list, offsetof(object_t, method)); + * + * @param this calling object + * @param offset offset of the method to invoke on objects + */ + void (*invoke) (linked_list_t *this, size_t offset); + + /** + * @brief Destroys a linked_list object. + * + * @param this calling object + */ + void (*destroy) (linked_list_t *this); + + /** + * @brief Destroys a list and its objects using the destructor. + * + * If a linked list and the contained objects should be destroyed, use + * destroy_offset. The supplied offset specifies the destructor to + * call on each object. The offset may be calculated using the offsetof + * macro, e.g.: list->destroy_offset(list, offsetof(object_t, destroy)); + * + * @param this calling object + * @param offset offset of the objects destructor + */ + void (*destroy_offset) (linked_list_t *this, size_t offset); + + /** + * @brief Destroys a list and its contents using a a cleanup function. + * + * If a linked list and its contents should get destroyed using a specific + * cleanup function, use destroy_function. This is useful when the + * list contains malloc()-ed blocks which should get freed, + * e.g.: list->destroy_function(list, free); + * + * @param this calling object + * @param function function to call on each object + */ + void (*destroy_function) (linked_list_t *this, void (*)(void*)); +}; + +/** + * @brief Creates an empty linked list object. + * + * @return linked_list_t object. + * + * @ingroup utils + */ +linked_list_t *linked_list_create(void); + + +#endif /*LINKED_LIST_H_*/ diff --git a/src/libstrongswan/utils/randomizer.c b/src/libstrongswan/utils/randomizer.c new file mode 100644 index 000000000..c15d108c7 --- /dev/null +++ b/src/libstrongswan/utils/randomizer.c @@ -0,0 +1,165 @@ +/** + * @file randomizer.c + * + * @brief Implementation of randomizer_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include "randomizer.h" + + +typedef struct private_randomizer_t private_randomizer_t; + +/** + * Private data of an randomizer_t object. + */ +struct private_randomizer_t { + + /** + * Public randomizer_t interface. + */ + randomizer_t public; + + /** + * @brief Reads a specific number of bytes from random or pseudo random device. + * + * @param this calling object + * @param pseudo_random TRUE, if from pseudo random bytes should be read, + * FALSE for true random bytes + * @param bytes number of bytes to read + * @param[out] buffer pointer to buffer where to write the data in. + * Size of buffer has to be at least bytes. + */ + status_t (*get_bytes_from_device) (private_randomizer_t *this,bool pseudo_random, size_t bytes, u_int8_t *buffer); +}; + + +/** + * Implementation of private_randomizer_t.get_bytes_from_device. + */ +static status_t get_bytes_from_device(private_randomizer_t *this,bool pseudo_random, size_t bytes, u_int8_t *buffer) +{ + size_t ndone; + int device; + size_t got; + char * device_name; + + device_name = pseudo_random ? DEV_URANDOM : DEV_RANDOM; + + device = open(device_name, 0); + if (device < 0) { + return FAILED; + } + ndone = 0; + + /* read until nbytes are read */ + while (ndone < bytes) + { + got = read(device, buffer + ndone, bytes - ndone); + if (got <= 0) { + close(device); + return FAILED; + } + ndone += got; + } + close(device); + return SUCCESS; +} + +/** + * Implementation of randomizer_t.get_random_bytes. + */ +static status_t get_random_bytes(private_randomizer_t *this,size_t bytes, u_int8_t *buffer) +{ + return this->get_bytes_from_device(this, FALSE, bytes, buffer); +} + +/** + * Implementation of randomizer_t.allocate_random_bytes. + */ +static status_t allocate_random_bytes(private_randomizer_t *this, size_t bytes, chunk_t *chunk) +{ + status_t status; + chunk->len = bytes; + chunk->ptr = malloc(bytes); + status = this->get_bytes_from_device(this, FALSE, bytes, chunk->ptr); + if (status != SUCCESS) + { + free(chunk->ptr); + } + return status; +} + +/** + * Implementation of randomizer_t.get_pseudo_random_bytes. + */ +static status_t get_pseudo_random_bytes(private_randomizer_t *this,size_t bytes, u_int8_t *buffer) +{ + return (this->get_bytes_from_device(this, TRUE, bytes, buffer)); +} + +/** + * Implementation of randomizer_t.allocate_pseudo_random_bytes. + */ +static status_t allocate_pseudo_random_bytes(private_randomizer_t *this, size_t bytes, chunk_t *chunk) +{ + status_t status; + chunk->len = bytes; + chunk->ptr = malloc(bytes); + status = this->get_bytes_from_device(this, TRUE, bytes, chunk->ptr); + if (status != SUCCESS) + { + free(chunk->ptr); + } + return status; +} + +/** + * Implementation of randomizer_t.destroy. + */ +static void destroy(private_randomizer_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +randomizer_t *randomizer_create(void) +{ + private_randomizer_t *this = malloc_thing(private_randomizer_t); + + /* public functions */ + this->public.get_random_bytes = (status_t (*) (randomizer_t *,size_t, u_int8_t *)) get_random_bytes; + this->public.allocate_random_bytes = (status_t (*) (randomizer_t *,size_t, chunk_t *)) allocate_random_bytes; + this->public.get_pseudo_random_bytes = (status_t (*) (randomizer_t *,size_t, u_int8_t *)) get_pseudo_random_bytes; + this->public.allocate_pseudo_random_bytes = (status_t (*) (randomizer_t *,size_t, chunk_t *)) allocate_pseudo_random_bytes; + this->public.destroy = (void (*) (randomizer_t *))destroy; + + /* private functions */ + this->get_bytes_from_device = get_bytes_from_device; + + return &(this->public); +} diff --git a/src/libstrongswan/utils/randomizer.h b/src/libstrongswan/utils/randomizer.h new file mode 100644 index 000000000..afbade059 --- /dev/null +++ b/src/libstrongswan/utils/randomizer.h @@ -0,0 +1,114 @@ +/** + * @file randomizer.h + * + * @brief Interface of randomizer_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef RANDOMIZER_H_ +#define RANDOMIZER_H_ + +typedef struct randomizer_t randomizer_t; + +#include <library.h> + +#ifndef DEV_RANDOM +/** + * Device to read real random bytes + */ +# define DEV_RANDOM "/dev/random" +#endif + +#ifndef DEV_URANDOM +/** + * Device to read pseudo random bytes + */ +# define DEV_URANDOM "/dev/urandom" +#endif + +/** + * @brief Class used to get random and pseudo random values. + * + * @b Constructors: + * - randomizer_create() + * + * @ingroup utils + */ +struct randomizer_t { + + /** + * @brief Reads a specific number of bytes from random device. + * + * @param this calling randomizer_t object + * @param bytes number of bytes to read + * @param[out] buffer pointer to buffer where to write the data in. + * Size of buffer has to be at least bytes. + * @return SUCCESS, or FAILED + */ + status_t (*get_random_bytes) (randomizer_t *this, size_t bytes, u_int8_t *buffer); + + /** + * @brief Allocates space and writes in random bytes. + * + * @param this calling randomizer_t object + * @param bytes number of bytes to allocate + * @param[out] chunk chunk which will hold the allocated random bytes + * @return SUCCESS, or FAILED + */ + status_t (*allocate_random_bytes) (randomizer_t *this, size_t bytes, chunk_t *chunk); + + /** + * @brief Reads a specific number of bytes from pseudo random device. + * + * @param this calling randomizer_t object + * @param bytes number of bytes to read + * @param[out] buffer pointer to buffer where to write the data in. + * size of buffer has to be at least bytes. + * @return SUCCESS, or FAILED + */ + status_t (*get_pseudo_random_bytes) (randomizer_t *this,size_t bytes, u_int8_t *buffer); + + /** + * @brief Allocates space and writes in pseudo random bytes. + * + * @param this calling randomizer_t object + * @param bytes number of bytes to allocate + * @param[out] chunk chunk which will hold the allocated random bytes + * @return SUCCESS, or FAILED + */ + status_t (*allocate_pseudo_random_bytes) (randomizer_t *this, size_t bytes, chunk_t *chunk); + + /** + * @brief Destroys a randomizer_t object. + * + * @param this randomizer_t object to destroy + */ + void (*destroy) (randomizer_t *this); +}; + +/** + * @brief Creates a randomizer_t object. + * + * @return created randomizer_t, or + * + * @ingroup utils + */ +randomizer_t *randomizer_create(void); + +#endif /*RANDOMIZER_H_*/ |