diff options
Diffstat (limited to 'src')
26 files changed, 1369 insertions, 190 deletions
diff --git a/src/conftest/README b/src/conftest/README index a1b1b3e3a..133ae603c 100644 --- a/src/conftest/README +++ b/src/conftest/README @@ -239,6 +239,7 @@ Currently, the following hooks are defined with the following options: rebuild_auth: rebuild AUTH payload, i.e. if ID payload changed reset_seq: Reset sequence numbers of an ESP SA delay: Seconds to delay reset after SA established + oseq: Sequence number to set, default is 0 set_critical: Set critical bit on existing payloads: request: yes to set in request, no in response id: IKEv2 message identifier of message to mangle payloads diff --git a/src/conftest/conftest.c b/src/conftest/conftest.c index bb4c5ed97..c2251effa 100644 --- a/src/conftest/conftest.c +++ b/src/conftest/conftest.c @@ -378,6 +378,22 @@ static void load_log_levels(file_logger_t *logger, char *section) } /** + * Load logger options for a logger from section + */ +static void load_logger_options(file_logger_t *logger, char *section) +{ + bool ike_name; + char *time_format; + + time_format = conftest->test->get_str(conftest->test, + "log.%s.time_format", NULL, section); + ike_name = conftest->test->get_bool(conftest->test, + "log.%s.ike_name", FALSE, section); + + logger->set_options(logger, time_format, ike_name); +} + +/** * Load logger configuration */ static void load_loggers(file_logger_t *logger) @@ -386,6 +402,10 @@ static void load_loggers(file_logger_t *logger) char *section; load_log_levels(logger, "stdout"); + load_logger_options(logger, "stdout"); + /* Re-add the logger to propagate configuration changes to the + * logging system */ + charon->bus->add_logger(charon->bus, &logger->logger); enumerator = conftest->test->create_section_enumerator(conftest->test, "log"); while (enumerator->enumerate(enumerator, §ion)) @@ -393,7 +413,7 @@ static void load_loggers(file_logger_t *logger) if (!streq(section, "stdout")) { logger = file_logger_create(section); - logger->set_options(logger, NULL, FALSE); + load_logger_options(logger, section); logger->open(logger, FALSE, FALSE); load_log_levels(logger, section); charon->bus->add_logger(charon->bus, &logger->logger); diff --git a/src/conftest/hooks/reset_seq.c b/src/conftest/hooks/reset_seq.c index 6fb7a2e4b..100977324 100644 --- a/src/conftest/hooks/reset_seq.c +++ b/src/conftest/hooks/reset_seq.c @@ -12,6 +12,27 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ +/* + * Copyright (C) 2012 achelos GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ #include "hook.h" @@ -40,21 +61,46 @@ struct private_reset_seq_t { * Delay for reset */ int delay; + + /** + * Sequence number to set for outgoing packages + */ + int oseq; +}; + +typedef struct reset_cb_data_t reset_cb_data_t; + +/** + * Data needed for the callback job + */ +struct reset_cb_data_t { + + /** + * The SA to modify + */ + struct xfrm_usersa_id usersa; + + /** + * Sequence number to set for outgoing packages + */ + int oseq; }; /** * Callback job */ -static job_requeue_t reset_cb(struct xfrm_usersa_id *data) +static job_requeue_t reset_cb(struct reset_cb_data_t *data) { netlink_buf_t request; struct nlmsghdr *hdr; struct xfrm_aevent_id *id; struct rtattr *rthdr; + struct xfrm_replay_state *rpstate; struct sockaddr_nl addr; int s, len; - DBG1(DBG_CFG, "resetting sequence number of SPI 0x%x", htonl(data->spi)); + DBG1(DBG_CFG, "setting sequence number of SPI 0x%x to %d", + htonl(data->usersa.spi), data->oseq); memset(&request, 0, sizeof(request)); @@ -66,13 +112,22 @@ static job_requeue_t reset_cb(struct xfrm_usersa_id *data) hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_aevent_id)); id = (struct xfrm_aevent_id*)NLMSG_DATA(hdr); - id->sa_id = *data; + id->sa_id = data->usersa; rthdr = XFRM_RTA(hdr, struct xfrm_aevent_id); rthdr->rta_type = XFRMA_REPLAY_VAL; rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state)); hdr->nlmsg_len += rthdr->rta_len; + /* xfrm_replay_state is the structure the kernel uses for + * replay detection, and the oseq element contains the + * sequence number for outgoing packets. Currently, this + * function sets the other elements seq (records the number of + * incoming packets) and bitmask to zero, but they could be + * adjusted in the same way as oseq if required. */ + rpstate = (struct xfrm_replay_state*)RTA_DATA(rthdr); + rpstate->oseq = data->oseq; + s = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); if (s == -1) { @@ -97,17 +152,21 @@ static job_requeue_t reset_cb(struct xfrm_usersa_id *data) static void schedule_reset_job(private_reset_seq_t *this, host_t *dst, u_int32_t spi) { - struct xfrm_usersa_id *data; + struct reset_cb_data_t *data; chunk_t chunk; INIT(data, - .spi = spi, - .family = dst->get_family(dst), - .proto = IPPROTO_ESP, + .usersa = { + .spi = spi, + .family = dst->get_family(dst), + .proto = IPPROTO_ESP, + }, + .oseq = this->oseq, ); chunk = dst->get_address(dst); - memcpy(&data->daddr, chunk.ptr, min(chunk.len, sizeof(xfrm_address_t))); + memcpy(&data->usersa.daddr, chunk.ptr, + min(chunk.len, sizeof(xfrm_address_t))); lib->scheduler->schedule_job(lib->scheduler, (job_t*)callback_job_create( @@ -149,6 +208,8 @@ hook_t *reset_seq_hook_create(char *name) }, .delay = conftest->test->get_int(conftest->test, "hooks.%s.delay", 10, name), + .oseq = conftest->test->get_int(conftest->test, + "hooks.%s.oseq", 0, name), ); return &this->hook; diff --git a/src/frontends/android/res/values-ru/arrays.xml b/src/frontends/android/res/values-ru/arrays.xml new file mode 100644 index 000000000..48a7219cc --- /dev/null +++ b/src/frontends/android/res/values-ru/arrays.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2012 Dmitry Korzhevin + + 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. +--> +<resources> + <!-- the order here must match the enum entries in VpnType.java --> + <string-array name="vpn_types"> + <item>IKEv2 EAP (Логин/Пароль)</item> + <item>Сертификат IKEv2</item> + </string-array> +</resources> diff --git a/src/frontends/android/res/values-ru/strings.xml b/src/frontends/android/res/values-ru/strings.xml new file mode 100644 index 000000000..afa21361b --- /dev/null +++ b/src/frontends/android/res/values-ru/strings.xml @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2012 Dmitry Korzhevin + + 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. +--> +<resources> + + <!-- Application --> + <string name="app_name">Клиент strongSwan VPN</string> + <string name="main_activity_name">strongSwan</string> + <string name="reload_trusted_certs">Обновить сертификат CA</string> + <string name="show_log">Журнал</string> + <string name="search">Поиск</string> + <string name="vpn_not_supported_title">VPN не поддерживается</string> + <string name="vpn_not_supported">Ваше устройство не поддерживат VPN приложение.\nПожалуйста свяжитесь с производителем.</string> + <string name="vpn_not_supported_during_lockdown">VPN соединения не поддерживаются в режиме lockdown.</string> + <string name="loading">Загрузка…</string> + <string name="profile_not_found">Профиль не найден</string> + <string name="strongswan_shortcut">Ссылка на strongSwan</string> + + <!-- Log view --> + <string name="log_title">Журнал</string> + <string name="send_log">Отправить журнал</string> + <string name="empty_log">Журнал пуст</string> + <string name="log_mail_subject">strongSwan %1$s журнал</string> + + <!-- VPN profile list --> + <string name="no_profiles">VPN профили не обнаружены.</string> + <string name="add_profile">Добавить VPN профиль</string> + <string name="edit_profile">Редактировать</string> + <string name="delete_profile">Удалить</string> + <string name="select_profiles">Выбрать профили</string> + <string name="profiles_deleted">Выбранные профили удалены</string> + <string name="no_profile_selected">Профили не выбраны</string> + <string name="one_profile_selected">Выбран профиль</string> + <string name="x_profiles_selected">%1$d прифиля(ей) выбрано"</string> + + <!-- VPN profile details --> + <string name="profile_edit_save">Сохранить</string> + <string name="profile_edit_cancel">Отмена</string> + <string name="profile_name_label">Название профиля:</string> + <string name="profile_name_hint">(адрес шлюза)</string> + <string name="profile_gateway_label">Шлюз:</string> + <string name="profile_vpn_type_label">Тип:</string> + <string name="profile_username_label">Логин:</string> + <string name="profile_password_label">Пароль:</string> + <string name="profile_password_hint">(спросить если нужно)</string> + <string name="profile_user_certificate_label">Сертификат пользователя:</string> + <string name="profile_user_select_certificate_label">Выбрать сертификат пользователя</string> + <string name="profile_user_select_certificate">Выбрать сертификат пользователя</string> + <string name="profile_ca_label">Сертификат CA:</string> + <string name="profile_ca_auto_label">Выбрать автоматически</string> + <string name="profile_ca_select_certificate_label">Выбрать сертификат CA</string> + <string name="profile_ca_select_certificate">Выбрать CA сертификат</string> + <!-- Warnings/Notifications in the details view --> + <string name="alert_text_no_input_gateway">Пожалуйста введите адрес шлюза</string> + <string name="alert_text_no_input_username">Пожалуйста введите имя пользователя</string> + <string name="alert_text_nocertfound_title">Не выбран сертификат CA</string> + <string name="alert_text_nocertfound">Пожалуйста выберите один <i>Выбрать автоматически</i></string> + + <!-- Trusted certificate selection --> + <string name="trusted_certs_title">Сертификаты CA</string> + <string name="no_certificates">Нет доступных сертификатов</string> + <string name="system_tab">Система</string> + <string name="user_tab">Пользователь</string> + + <!-- VPN state fragment --> + <string name="state_label">Статус:</string> + <string name="profile_label">Профиль:</string> + <string name="disconnect">Отключить</string> + <string name="state_connecting">Соединение…</string> + <string name="state_connected">Соединен</string> + <string name="state_disconnecting">Отключение…</string> + <string name="state_disabled">Нет активных VPN</string> + <string name="state_error">Ошибка</string> + + <!-- Dialogs --> + <string name="login_title">Введите пароль для соединения</string> + <string name="login_confirm">Соединить</string> + <string name="error_introduction">Ошибка подключения к VPN:</string> + <string name="error_lookup_failed">Не найден адрес шлюза.</string> + <string name="error_unreachable">Шлюз недоступен.</string> + <string name="error_peer_auth_failed">Ошибка авторизаци при подключении к шлюзу.</string> + <string name="error_auth_failed">Ошибка авторизации пользователя.</string> + <string name="error_generic">Неизвестная ошибка.</string> + <string name="connecting_title">Подключение: %1$s</string> + <string name="connecting_message">Подключение к VPN с \""%1$s\".</string> + <string name="vpn_connected">Соединение с VPN установлено</string> + <string name="vpn_profile_connected">Подключение к этому профилю VPN уже существует!</string> + <string name="reconnect">Переподключить</string> + <string name="connect_profile_question">Подключить %1$s?</string> + <string name="replaces_active_connection">Это заменит ваше текущее VPN соединение!</string> + <string name="connect">Соединить</string> + +</resources> + diff --git a/src/frontends/android/res/values-ua/arrays.xml b/src/frontends/android/res/values-ua/arrays.xml new file mode 100644 index 000000000..bd4366405 --- /dev/null +++ b/src/frontends/android/res/values-ua/arrays.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2012 Dmitry Korzhevin + + 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. +--> +<resources> + <!-- the order here must match the enum entries in VpnType.java --> + <string-array name="vpn_types"> + <item>IKEv2 EAP (Логін/Пароль)</item> + <item>Сертифікати IKEv2</item> + </string-array> +</resources> diff --git a/src/frontends/android/res/values-ua/strings.xml b/src/frontends/android/res/values-ua/strings.xml new file mode 100644 index 000000000..cb3ebee06 --- /dev/null +++ b/src/frontends/android/res/values-ua/strings.xml @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2012 Dmitry Korzhevin + Copyright (C) 2013 Pavel Kopchyk + + 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. +--> +<resources> + + <!-- Application --> + <string name="app_name">strongSwan VPN клієнт</string> + <string name="main_activity_name">strongSwan</string> + <string name="reload_trusted_certs">Перезавантажити CA сертифікати</string> + <string name="show_log">Перегляд журналу</string> + <string name="search">Пошук</string> + <string name="vpn_not_supported_title">VPN не підтримуеться</string> + <string name="vpn_not_supported">Ваш пристрій не підтримує VPN.\nЗв'яжіться з виробником.</string> + <string name="vpn_not_supported_during_lockdown">VPN з'єднання не пітримується у режимі lockdown.</string> + <string name="loading">Завантаження…</string> + <string name="profile_not_found">Профіль не знайдено</string> + <string name="strongswan_shortcut">strongSwan посилання</string> + + <!-- Log view --> + <string name="log_title">Журнал</string> + <string name="send_log">Відправити файл журналу</string> + <string name="empty_log">Журнал порожній</string> + <string name="log_mail_subject">strongSwan %1$s файл журналу</string> + + <!-- VPN profile list --> + <string name="no_profiles">Немає VPN профілів</string> + <string name="add_profile">Додати VPN профіль</string> + <string name="edit_profile">Редагувати</string> + <string name="delete_profile">Видалити</string> + <string name="select_profiles">Обрати профіль</string> + <string name="profiles_deleted">Обрані профілі видалено</string> + <string name="no_profile_selected">Профіль не обрано</string> + <string name="one_profile_selected">Один профіль обрано</string> + <string name="x_profiles_selected">%1$d профілів обрано"</string> + + <!-- VPN profile details --> + <string name="profile_edit_save">Зберегти</string> + <string name="profile_edit_cancel">Відміна</string> + <string name="profile_name_label">Назва профілю:</string> + <string name="profile_name_hint">(використовувати адресу шлюза)</string> + <string name="profile_gateway_label">Шлюз:</string> + <string name="profile_vpn_type_label">Тип:</string> + <string name="profile_username_label">Логін:</string> + <string name="profile_password_label">Пароль:</string> + <string name="profile_password_hint">(запитати якщо потрібно)</string> + <string name="profile_user_certificate_label">Сертифікат користувача:</string> + <string name="profile_user_select_certificate_label">Виберіть сертифікат користувача</string> + <string name="profile_user_select_certificate">Вибрати спеціальний сертифікат користувача</string> + <string name="profile_ca_label">Сертифікат CA:</string> + <string name="profile_ca_auto_label">Вибрати автоматично</string> + <string name="profile_ca_select_certificate_label">Вибрати сертифікат CA</string> + <string name="profile_ca_select_certificate">Вибрати спеціальний сертифікат CA</string> + <!-- Warnings/Notifications in the details view --> + <string name="alert_text_no_input_gateway">Введіть адресу шлюза тут</string> + <string name="alert_text_no_input_username">Введіть ім'я користувача тут</string> + <string name="alert_text_nocertfound_title">Не вибрано сертифікат CA</string> + <string name="alert_text_nocertfound">Будь ласка виберіть один <i>Вибрати автоматично</i></string> + + <!-- Trusted certificate selection --> + <string name="trusted_certs_title">Сертифікати CA</string> + <string name="no_certificates">Немає сертифікатів</string> + <string name="system_tab">Система</string> + <string name="user_tab">Користувач</string> + + <!-- VPN state fragment --> + <string name="state_label">Статус:</string> + <string name="profile_label">Профіль:</string> + <string name="disconnect">Роз'єднати</string> + <string name="state_connecting">Підключення…</string> + <string name="state_connected">Підключений</string> + <string name="state_disconnecting">Роз'єднання…</string> + <string name="state_disabled">Немає активних VPN</string> + <string name="state_error">Помилка</string> + + <!-- Dialogs --> + <string name="login_title">Введіть пароль для з'єднання</string> + <string name="login_confirm">Підключити</string> + <string name="error_introduction">Помилка підлючення VPN:</string> + <string name="error_lookup_failed">Помилка пошуку адреси шлюза.</string> + <string name="error_unreachable">Немає зв'язку зі шлюзом.</string> + <string name="error_peer_auth_failed">Помилка перевірки данних аутентифікації шлюза.</string> + <string name="error_auth_failed">Помилка аутентифікації користувача.</string> + <string name="error_generic">Невідома помилка під час підключення.</string> + <string name="connecting_title">Підключення: %1$s</string> + <string name="connecting_message">Підключення VPN з \""%1$s\".</string> + <string name="vpn_connected">VPN підключено</string> + <string name="vpn_profile_connected">Цей VPN профіль зараз підключений!</string> + <string name="reconnect">Перепідключитися</string> + <string name="connect_profile_question">Підключити %1$s?</string> + <string name="replaces_active_connection">Ця дія замінить ваше поточне VPN з'єднання!</string> + <string name="connect">Підключити</string> + +</resources> + diff --git a/src/libcharon/encoding/payloads/cert_payload.c b/src/libcharon/encoding/payloads/cert_payload.c index 3a230b91e..a32f5705d 100644 --- a/src/libcharon/encoding/payloads/cert_payload.c +++ b/src/libcharon/encoding/payloads/cert_payload.c @@ -234,6 +234,23 @@ METHOD(cert_payload_t, get_cert, certificate_t*, BUILD_BLOB_ASN1_DER, this->data, BUILD_END); } +METHOD(cert_payload_t, get_container, container_t*, + private_cert_payload_t *this) +{ + int type; + + switch (this->encoding) + { + case ENC_PKCS7_WRAPPED_X509: + type = CONTAINER_PKCS7; + break; + default: + return NULL; + } + return lib->creds->create(lib->creds, CRED_CONTAINER, type, + BUILD_BLOB_ASN1_DER, this->data, BUILD_END); +} + METHOD(cert_payload_t, get_hash, chunk_t, private_cert_payload_t *this) { @@ -289,6 +306,7 @@ cert_payload_t *cert_payload_create(payload_type_t type) .destroy = _destroy, }, .get_cert = _get_cert, + .get_container = _get_container, .get_cert_encoding = _get_cert_encoding, .get_hash = _get_hash, .get_url = _get_url, diff --git a/src/libcharon/encoding/payloads/cert_payload.h b/src/libcharon/encoding/payloads/cert_payload.h index 19ed2ccd2..834f35d60 100644 --- a/src/libcharon/encoding/payloads/cert_payload.h +++ b/src/libcharon/encoding/payloads/cert_payload.h @@ -28,10 +28,11 @@ typedef enum cert_encoding_t cert_encoding_t; #include <library.h> #include <credentials/certificates/certificate.h> +#include <credentials/containers/container.h> #include <encoding/payloads/payload.h> /** - * Certifcate encodings, as in RFC4306 + * Certificate encodings, as in RFC4306 */ enum cert_encoding_t { ENC_PKCS7_WRAPPED_X509 = 1, @@ -65,13 +66,20 @@ struct cert_payload_t { payload_t payload_interface; /** - * Get the playoads encoded certifcate. + * Get the payloads encoded certificate. * - * @return certifcate copy + * @return certificate copy */ certificate_t *(*get_cert)(cert_payload_t *this); /** + * Get the payloads certificate container. + * + * @return container copy + */ + container_t *(*get_container)(cert_payload_t *this); + + /** * Get the encoding of the certificate. * * @return encoding diff --git a/src/libcharon/encoding/payloads/certreq_payload.h b/src/libcharon/encoding/payloads/certreq_payload.h index cce71c0ad..2915decf3 100644 --- a/src/libcharon/encoding/payloads/certreq_payload.h +++ b/src/libcharon/encoding/payloads/certreq_payload.h @@ -56,7 +56,7 @@ struct certreq_payload_t { /** * Add a certificates keyid to the payload (IKEv2 only). * - * @param keyid keyid of the trusted certifcate + * @param keyid keyid of the trusted certificate * @return */ void (*add_keyid)(certreq_payload_t *this, chunk_t keyid); diff --git a/src/libcharon/encoding/payloads/eap_payload.c b/src/libcharon/encoding/payloads/eap_payload.c index dd2e25795..f2f35aa69 100644 --- a/src/libcharon/encoding/payloads/eap_payload.c +++ b/src/libcharon/encoding/payloads/eap_payload.c @@ -410,14 +410,15 @@ eap_payload_t *eap_payload_create_nak(u_int8_t identifier, eap_type_t type, eap_type_t reg_type; u_int32_t reg_vendor; bio_writer_t *writer; - chunk_t length, data; + chunk_t data; bool added_any = FALSE, found_vendor = FALSE; eap_payload_t *payload; writer = bio_writer_create(12); writer->write_uint8(writer, EAP_RESPONSE); writer->write_uint8(writer, identifier); - length = writer->skip(writer, 2); + /* write zero length, we update it once we know the length */ + writer->write_uint16(writer, 0); write_type(writer, EAP_NAK, 0, expanded); @@ -453,10 +454,9 @@ eap_payload_t *eap_payload_create_nak(u_int8_t identifier, eap_type_t type, /* set length */ data = writer->get_buf(writer); - htoun16(length.ptr, data.len); + htoun16(data.ptr + offsetof(eap_packet_t, length), data.len); payload = eap_payload_create_data(data); writer->destroy(writer); return payload; } - diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index 0ab94f2a7..112c32d41 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -443,7 +443,7 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this, cfg = auth_cfg_create(); - /* add identity and peer certifcate */ + /* add identity and peer certificate */ identity = identification_create_from_string(id); if (cert) { diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c index d48484f09..43a0aaa36 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c @@ -13,6 +13,28 @@ * for more details. */ +/* + * Copyright (C) 2013 Volker Rümelin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + #include "isakmp_cert_pre.h" #include <daemon.h> @@ -21,6 +43,7 @@ #include <encoding/payloads/sa_payload.h> #include <encoding/payloads/certreq_payload.h> #include <credentials/certificates/x509.h> +#include <credentials/containers/pkcs7.h> typedef struct private_isakmp_cert_pre_t private_isakmp_cert_pre_t; @@ -132,7 +155,106 @@ static void process_certreqs(private_isakmp_cert_pre_t *this, message_t *message } /** - * Import receuved certificates + * Process an X509 certificate payload + */ +static void process_x509(cert_payload_t *payload, auth_cfg_t *auth, bool *first) +{ + certificate_t *cert; + + cert = payload->get_cert(payload); + if (cert) + { + if (*first) + { /* the first is an end entity certificate */ + DBG1(DBG_IKE, "received end entity cert \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert); + *first = FALSE; + } + else + { + DBG1(DBG_IKE, "received issuer cert \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_IM_CERT, cert); + } + } +} + +/** + * Process a CRL certificate payload + */ +static void process_crl(cert_payload_t *payload, auth_cfg_t *auth) +{ + certificate_t *cert; + + cert = payload->get_cert(payload); + if (cert) + { + DBG1(DBG_IKE, "received CRL \"%Y\"", cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert); + } +} + +/** + * Process a PKCS7 certificate payload + */ +static void process_pkcs7(cert_payload_t *payload, auth_cfg_t *auth) +{ + enumerator_t *enumerator; + container_t *container; + certificate_t *cert; + pkcs7_t *pkcs7; + + container = payload->get_container(payload); + if (!container) + { + return; + } + switch (container->get_type(container)) + { + case CONTAINER_PKCS7_DATA: + case CONTAINER_PKCS7_SIGNED_DATA: + case CONTAINER_PKCS7_ENVELOPED_DATA: + break; + default: + container->destroy(container); + return; + } + + pkcs7 = (pkcs7_t *)container; + enumerator = pkcs7->create_cert_enumerator(pkcs7); + while (enumerator->enumerate(enumerator, &cert)) + { + if (cert->get_type(cert) == CERT_X509) + { + x509_t *x509 = (x509_t*)cert; + + if (x509->get_flags(x509) & X509_CA) + { + DBG1(DBG_IKE, "received issuer cert \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_IM_CERT, cert->get_ref(cert)); + } + else + { + DBG1(DBG_IKE, "received end entity cert \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert->get_ref(cert)); + } + } + else + { + DBG1(DBG_IKE, "received unsupported cert type %N", + certificate_type_names, cert->get_type(cert)); + } + } + enumerator->destroy(enumerator); + + container->destroy(container); +} + +/** + * Import received certificates */ static void process_certs(private_isakmp_cert_pre_t *this, message_t *message) { @@ -150,7 +272,6 @@ static void process_certs(private_isakmp_cert_pre_t *this, message_t *message) { cert_payload_t *cert_payload; cert_encoding_t encoding; - certificate_t *cert; cert_payload = (cert_payload_t*)payload; encoding = cert_payload->get_cert_encoding(cert_payload); @@ -158,36 +279,14 @@ static void process_certs(private_isakmp_cert_pre_t *this, message_t *message) switch (encoding) { case ENC_X509_SIGNATURE: - { - cert = cert_payload->get_cert(cert_payload); - if (cert) - { - if (first) - { /* the first is an end entity certificate */ - DBG1(DBG_IKE, "received end entity cert \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert); - first = FALSE; - } - else - { - DBG1(DBG_IKE, "received issuer cert \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_HELPER_IM_CERT, cert); - } - } + process_x509(cert_payload, auth, &first); break; - } case ENC_CRL: - cert = cert_payload->get_cert(cert_payload); - if (cert) - { - DBG1(DBG_IKE, "received CRL \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert); - } + process_crl(cert_payload, auth); break; case ENC_PKCS7_WRAPPED_X509: + process_pkcs7(cert_payload, auth); + break; case ENC_PGP: case ENC_DNS_SIGNED_KEY: case ENC_KERBEROS_TOKEN: diff --git a/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c index 60e878777..2cbe8f8c5 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c +++ b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c @@ -57,6 +57,72 @@ struct private_ike_cert_pre_t { }; /** + * Process a single certificate request payload + */ +static void process_certreq(private_ike_cert_pre_t *this, + certreq_payload_t *certreq, auth_cfg_t *auth) +{ + enumerator_t *enumerator; + u_int unknown = 0; + chunk_t keyid; + + this->ike_sa->set_condition(this->ike_sa, COND_CERTREQ_SEEN, TRUE); + + if (certreq->get_cert_type(certreq) != CERT_X509) + { + DBG1(DBG_IKE, "cert payload %N not supported - ignored", + certificate_type_names, certreq->get_cert_type(certreq)); + return; + } + + enumerator = certreq->create_keyid_enumerator(certreq); + while (enumerator->enumerate(enumerator, &keyid)) + { + identification_t *id; + certificate_t *cert; + + id = identification_create_from_encoding(ID_KEY_ID, keyid); + cert = lib->credmgr->get_cert(lib->credmgr, + CERT_X509, KEY_ANY, id, TRUE); + if (cert) + { + DBG1(DBG_IKE, "received cert request for \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_RULE_CA_CERT, cert); + } + else + { + DBG2(DBG_IKE, "received cert request for unknown ca with keyid %Y", + id); + unknown++; + } + id->destroy(id); + } + enumerator->destroy(enumerator); + if (unknown) + { + DBG1(DBG_IKE, "received %u cert requests for an unknown ca", + unknown); + } +} + +/** + * Process a single notify payload + */ +static void process_notify(private_ike_cert_pre_t *this, + notify_payload_t *notify) +{ + switch (notify->get_notify_type(notify)) + { + case HTTP_CERT_LOOKUP_SUPPORTED: + this->ike_sa->enable_extension(this->ike_sa, EXT_HASH_AND_URL); + break; + default: + break; + } +} + +/** * read certificate requests */ static void process_certreqs(private_ike_cert_pre_t *this, message_t *message) @@ -73,62 +139,11 @@ static void process_certreqs(private_ike_cert_pre_t *this, message_t *message) switch (payload->get_type(payload)) { case CERTIFICATE_REQUEST: - { - certreq_payload_t *certreq = (certreq_payload_t*)payload; - enumerator_t *enumerator; - u_int unknown = 0; - chunk_t keyid; - - this->ike_sa->set_condition(this->ike_sa, COND_CERTREQ_SEEN, TRUE); - - if (certreq->get_cert_type(certreq) != CERT_X509) - { - DBG1(DBG_IKE, "cert payload %N not supported - ignored", - certificate_type_names, certreq->get_cert_type(certreq)); - break; - } - enumerator = certreq->create_keyid_enumerator(certreq); - while (enumerator->enumerate(enumerator, &keyid)) - { - identification_t *id; - certificate_t *cert; - - id = identification_create_from_encoding(ID_KEY_ID, keyid); - cert = lib->credmgr->get_cert(lib->credmgr, - CERT_X509, KEY_ANY, id, TRUE); - if (cert) - { - DBG1(DBG_IKE, "received cert request for \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_RULE_CA_CERT, cert); - } - else - { - DBG2(DBG_IKE, "received cert request for unknown ca " - "with keyid %Y", id); - unknown++; - } - id->destroy(id); - } - enumerator->destroy(enumerator); - if (unknown) - { - DBG1(DBG_IKE, "received %u cert requests for an unknown ca", - unknown); - } + process_certreq(this, (certreq_payload_t*)payload, auth); break; - } case NOTIFY: - { - notify_payload_t *notify = (notify_payload_t*)payload; - - /* we only handle one type of notify here */ - if (notify->get_notify_type(notify) == HTTP_CERT_LOOKUP_SUPPORTED) - { - this->ike_sa->enable_extension(this->ike_sa, EXT_HASH_AND_URL); - } + process_notify(this, (notify_payload_t*)payload); break; - } default: /* ignore other payloads here, these are handled elsewhere */ break; @@ -177,7 +192,75 @@ static certificate_t *try_get_cert(cert_payload_t *cert_payload) } /** - * import certificates + * Process a X509 certificate payload + */ +static void process_x509(cert_payload_t *payload, auth_cfg_t *auth, + cert_encoding_t encoding, bool *first) +{ + certificate_t *cert; + char *url; + + cert = try_get_cert(payload); + if (cert) + { + if (*first) + { /* the first is an end entity certificate */ + DBG1(DBG_IKE, "received end entity cert \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert); + *first = FALSE; + } + else + { + DBG1(DBG_IKE, "received issuer cert \"%Y\"", + cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_IM_CERT, cert); + } + } + else if (encoding == ENC_X509_HASH_AND_URL) + { + /* we fetch the certificate not yet, but only if + * it is really needed during authentication */ + url = payload->get_url(payload); + if (!url) + { + DBG1(DBG_IKE, "received invalid hash-and-url " + "encoded cert, ignore"); + return; + } + url = strdup(url); + if (first) + { /* first URL is for an end entity certificate */ + DBG1(DBG_IKE, "received hash-and-url for end entity cert \"%s\"", + url); + auth->add(auth, AUTH_HELPER_SUBJECT_HASH_URL, url); + first = FALSE; + } + else + { + DBG1(DBG_IKE, "received hash-and-url for issuer cert \"%s\"", url); + auth->add(auth, AUTH_HELPER_IM_HASH_URL, url); + } + } +} + +/** + * Process a CRL certificate payload + */ +static void process_crl(cert_payload_t *payload, auth_cfg_t *auth) +{ + certificate_t *cert; + + cert = payload->get_cert(payload); + if (cert) + { + DBG1(DBG_IKE, "received CRL \"%Y\"", cert->get_subject(cert)); + auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert); + } +} + +/** + * Process certificate payloads */ static void process_certs(private_ike_cert_pre_t *this, message_t *message) { @@ -195,8 +278,6 @@ static void process_certs(private_ike_cert_pre_t *this, message_t *message) { cert_payload_t *cert_payload; cert_encoding_t encoding; - certificate_t *cert; - char *url; cert_payload = (cert_payload_t*)payload; encoding = cert_payload->get_cert_encoding(cert_payload); @@ -204,70 +285,18 @@ static void process_certs(private_ike_cert_pre_t *this, message_t *message) switch (encoding) { case ENC_X509_HASH_AND_URL: - { if (!this->do_http_lookup) { - DBG1(DBG_IKE, "received hash-and-url encoded cert, but" - " we don't accept them, ignore"); + DBG1(DBG_IKE, "received hash-and-url encoded cert, but " + "we don't accept them, ignore"); break; } /* FALL */ - } case ENC_X509_SIGNATURE: - { - cert = try_get_cert(cert_payload); - if (cert) - { - if (first) - { /* the first is an end entity certificate */ - DBG1(DBG_IKE, "received end entity cert \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert); - first = FALSE; - } - else - { - DBG1(DBG_IKE, "received issuer cert \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_HELPER_IM_CERT, cert); - } - } - else if (encoding == ENC_X509_HASH_AND_URL) - { - /* we fetch the certificate not yet, but only if - * it is really needed during authentication */ - url = cert_payload->get_url(cert_payload); - if (!url) - { - DBG1(DBG_IKE, "received invalid hash-and-url " - "encoded cert, ignore"); - break; - } - url = strdup(url); - if (first) - { /* first URL is for an end entity certificate */ - DBG1(DBG_IKE, "received hash-and-url for end" - " entity cert \"%s\"", url); - auth->add(auth, AUTH_HELPER_SUBJECT_HASH_URL, url); - first = FALSE; - } - else - { - DBG1(DBG_IKE, "received hash-and-url for issuer" - " cert \"%s\"", url); - auth->add(auth, AUTH_HELPER_IM_HASH_URL, url); - } - } + process_x509(cert_payload, auth, encoding, &first); break; - } case ENC_CRL: - cert = cert_payload->get_cert(cert_payload); - if (cert) - { - DBG1(DBG_IKE, "received CRL \"%Y\"", - cert->get_subject(cert)); - auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert); - } + process_crl(cert_payload, auth); break; case ENC_PKCS7_WRAPPED_X509: case ENC_PGP: diff --git a/src/libimcv/imv/imv_msg.c b/src/libimcv/imv/imv_msg.c index 81209ce57..4ed19dd13 100644 --- a/src/libimcv/imv/imv_msg.c +++ b/src/libimcv/imv/imv_msg.c @@ -195,6 +195,12 @@ METHOD(imv_msg_t, send_assessment, TNC_Result, char *lang_code = NULL, *uri = NULL; enumerator_t *e; + /* Remove any attributes that have already been constructed */ + while (this->attr_list->remove_last(this->attr_list, (void**)&attr) == SUCCESS) + { + attr->destroy(attr); + } + /* Send an IETF Assessment Result attribute if enabled */ if (lib->settings->get_bool(lib->settings, "libimcv.assessment_result", TRUE)) @@ -306,17 +312,6 @@ METHOD(imv_msg_t, receive, TNC_Result, return TNC_RESULT_SUCCESS; } -METHOD(imv_msg_t, delete_attributes, void, - private_imv_msg_t *this) -{ - pa_tnc_attr_t *attr; - - while (this->attr_list->remove_last(this->attr_list, (void**)&attr) == SUCCESS) - { - attr->destroy(attr); - } -} - METHOD(imv_msg_t, create_attribute_enumerator, enumerator_t*, private_imv_msg_t *this) { @@ -361,7 +356,6 @@ imv_msg_t *imv_msg_create(imv_agent_t *agent, imv_state_t *state, .send_assessment = _send_assessment, .receive = _receive, .add_attribute = _add_attribute, - .delete_attributes = _delete_attributes, .create_attribute_enumerator = _create_attribute_enumerator, .get_encoding = _get_encoding, .destroy = _destroy, diff --git a/src/libimcv/imv/imv_msg.h b/src/libimcv/imv/imv_msg.h index 92347934f..b639712e8 100644 --- a/src/libimcv/imv/imv_msg.h +++ b/src/libimcv/imv/imv_msg.h @@ -85,12 +85,6 @@ struct imv_msg_t { void (*add_attribute)(imv_msg_t *this, pa_tnc_attr_t *attr); /** - * Delete all PA-TNC attributes in the send queue - * - */ - void (*delete_attributes)(imv_msg_t *this); - - /** * Enumerator over PA-TNC attributes contained in the PA-TNC message * * @return PA-TNC attribute enumerator diff --git a/src/libimcv/plugins/imv_os/pacman.sh b/src/libimcv/plugins/imv_os/pacman.sh index a17a0839a..e9134ea5d 100755 --- a/src/libimcv/plugins/imv_os/pacman.sh +++ b/src/libimcv/plugins/imv_os/pacman.sh @@ -5,6 +5,10 @@ DEBIAN=http://packages.debian.org UBUNTU=http://packages.ubuntu.com UBUNTU_VERSIONS="quantal precise oneiric lucid" PACKAGES=allpackages?format=txt.gz +PACMAN=/usr/libexec/ipsec/pacman +DIR=/etc/pts + +cd $DIR for v in $UBUNTU_VERSIONS do @@ -15,22 +19,22 @@ done wget $DEBIAN/stable/$PACKAGES -O $DATE-squeeze.txt.gz gunzip *.gz -ipsec pacman --product "Ubuntu 12.10" --file $DATE-quantal.txt +$PACMAN --product "Ubuntu 12.10" --file $DATE-quantal.txt echo -ipsec pacman --product "Ubuntu 12.10" --file $DATE-quantal-updates.txt --update +$PACMAN --product "Ubuntu 12.10" --file $DATE-quantal-updates.txt --update echo -ipsec pacman --product "Ubuntu 12.04" --file $DATE-precise.txt +$PACMAN --product "Ubuntu 12.04" --file $DATE-precise.txt echo -ipsec pacman --product "Ubuntu 12.04" --file $DATE-precise-updates.txt --update +$PACMAN --product "Ubuntu 12.04" --file $DATE-precise-updates.txt --update echo -ipsec pacman --product "Ubuntu 11.10" --file $DATE-oneiric.txt +$PACMAN --product "Ubuntu 11.10" --file $DATE-oneiric.txt echo -ipsec pacman --product "Ubuntu 11.10" --file $DATE-oneiric-updates.txt --update +$PACMAN --product "Ubuntu 11.10" --file $DATE-oneiric-updates.txt --update echo -ipsec pacman --product "Ubuntu 10.04" --file $DATE-lucid.txt +$PACMAN --product "Ubuntu 10.04" --file $DATE-lucid.txt echo -ipsec pacman --product "Ubuntu 10.04" --file $DATE-lucid-updates.txt --update +$PACMAN --product "Ubuntu 10.04" --file $DATE-lucid-updates.txt --update echo -ipsec pacman --product "Debian squeeze" --file $DATE-squeeze.txt +$PACMAN --product "Debian squeeze" --file $DATE-squeeze.txt cp config.db config.db-$DATE diff --git a/src/libpts/plugins/imv_attestation/imv_attestation.c b/src/libpts/plugins/imv_attestation/imv_attestation.c index 237300f31..3c5488eba 100644 --- a/src/libpts/plugins/imv_attestation/imv_attestation.c +++ b/src/libpts/plugins/imv_attestation/imv_attestation.c @@ -285,9 +285,8 @@ static TNC_Result receive_message(imv_state_t *state, imv_msg_t *in_msg) if (fatal_error || result != TNC_RESULT_SUCCESS) { - out_msg->delete_attributes(out_msg); state->set_recommendation(state, - TNC_IMV_ACTION_RECOMMENDATION_ISOLATE, + TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, TNC_IMV_EVALUATION_RESULT_ERROR); result = out_msg->send_assessment(out_msg); out_msg->destroy(out_msg); @@ -315,7 +314,6 @@ static TNC_Result receive_message(imv_state_t *state, imv_msg_t *in_msg) state->set_recommendation(state, TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, TNC_IMV_EVALUATION_RESULT_ERROR); - out_msg->delete_attributes(out_msg); result = out_msg->send_assessment(out_msg); out_msg->destroy(out_msg); if (result != TNC_RESULT_SUCCESS) diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 243ff5504..9c4665eeb 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -204,6 +204,13 @@ if MONOLITHIC endif endif +if USE_RDRAND + SUBDIRS += plugins/rdrand +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/rdrand/libstrongswan-rdrand.la +endif +endif + if USE_RANDOM SUBDIRS += plugins/random if MONOLITHIC diff --git a/src/libstrongswan/bio/bio_writer.h b/src/libstrongswan/bio/bio_writer.h index 57a5c3d38..2ac4f3556 100644 --- a/src/libstrongswan/bio/bio_writer.h +++ b/src/libstrongswan/bio/bio_writer.h @@ -126,8 +126,11 @@ struct bio_writer_t { void (*wrap32)(bio_writer_t *this); /** - * Skips len bytes in the buffer before the next data is written, returns - * a chunk covering the skipped bytes. + * Skips len bytes in the buffer, return chunk of skipped data. + * + * The returned chunk is not valid after calling any other writer function + * (except get_buf()), because a buffer reallocation might move the + * internal buffer to a different memory location! * * @param len number of bytes to skip * @return chunk pointing to skipped bytes in the internal buffer diff --git a/src/libstrongswan/plugins/openssl/openssl_pkcs7.c b/src/libstrongswan/plugins/openssl/openssl_pkcs7.c index 6cd243c61..663397e59 100644 --- a/src/libstrongswan/plugins/openssl/openssl_pkcs7.c +++ b/src/libstrongswan/plugins/openssl/openssl_pkcs7.c @@ -13,6 +13,10 @@ * for more details. */ +#include <openssl/opensslconf.h> + +#ifndef OPENSSL_NO_CMS + #include "openssl_pkcs7.h" #include "openssl_util.h" @@ -782,3 +786,5 @@ pkcs7_t *openssl_pkcs7_load(container_type_t type, va_list args) } return NULL; } + +#endif /* OPENSSL_NO_CMS */ diff --git a/src/libstrongswan/plugins/rdrand/Makefile.am b/src/libstrongswan/plugins/rdrand/Makefile.am new file mode 100644 index 000000000..4be7b7215 --- /dev/null +++ b/src/libstrongswan/plugins/rdrand/Makefile.am @@ -0,0 +1,16 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-rdrand.la +else +plugin_LTLIBRARIES = libstrongswan-rdrand.la +endif + +libstrongswan_rdrand_la_SOURCES = \ + rdrand_plugin.h rdrand_plugin.c \ + rdrand_rng.h rdrand_rng.c + +libstrongswan_rdrand_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/rdrand/rdrand_plugin.c b/src/libstrongswan/plugins/rdrand/rdrand_plugin.c new file mode 100644 index 000000000..4bdfc258e --- /dev/null +++ b/src/libstrongswan/plugins/rdrand/rdrand_plugin.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * 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 "rdrand_plugin.h" +#include "rdrand_rng.h" + +#include <stdio.h> + +#include <library.h> +#include <utils/debug.h> + +typedef struct private_rdrand_plugin_t private_rdrand_plugin_t; +typedef enum cpuid_feature_t cpuid_feature_t; + +/** + * private data of rdrand_plugin + */ +struct private_rdrand_plugin_t { + + /** + * public functions + */ + rdrand_plugin_t public; +}; + +/** + * CPU feature flags, returned via cpuid(1) + */ +enum cpuid_feature_t { + CPUID_RDRAND = (1<<30), +}; + +/** + * Get cpuid for info, return eax, ebx, ecx and edx. + * -fPIC requires to save ebx on IA-32. + */ +static void cpuid(u_int op, u_int *a, u_int *b, u_int *c, u_int *d) +{ +#ifdef __x86_64__ + asm("cpuid" : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d) : "a" (op)); +#else /* __i386__ */ + asm("pushl %%ebx;" + "cpuid;" + "movl %%ebx, %1;" + "popl %%ebx;" + : "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d) : "a" (op)); +#endif /* __x86_64__ / __i386__*/ +} + +/** + * Check if we have RDRAND instruction + */ +static bool have_rdrand() +{ + char vendor[3 * sizeof(u_int32_t) + 1]; + u_int a, b, c, d; + + cpuid(0, &a, &b, &c, &d); + /* VendorID string is in b-d-c (yes, in this order) */ + snprintf(vendor, sizeof(vendor), "%.4s%.4s%.4s", &b, &d, &c); + + /* check if we have an Intel CPU */ + if (streq(vendor, "GenuineIntel")) + { + cpuid(1, &a, &b, &c, &d); + if (c & CPUID_RDRAND) + { + DBG1(DBG_LIB, "detected RDRAND support on %s CPU", vendor); + return TRUE; + } + } + DBG1(DBG_LIB, "no RDRAND support on %s CPU, disabled", vendor); + return FALSE; +} + +METHOD(plugin_t, get_name, char*, + private_rdrand_plugin_t *this) +{ + return "rdrand"; +} + +METHOD(plugin_t, get_features, int, + private_rdrand_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_REGISTER(RNG, rdrand_rng_create), + PLUGIN_PROVIDE(RNG, RNG_WEAK), + PLUGIN_PROVIDE(RNG, RNG_STRONG), + PLUGIN_PROVIDE(RNG, RNG_TRUE), + PLUGIN_DEPENDS(CRYPTER, ENCR_AES_CBC, 16), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_rdrand_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *rdrand_plugin_create() +{ + private_rdrand_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .reload = (void*)return_false, + .destroy = _destroy, + }, + }, + ); + + if (have_rdrand()) + { + this->public.plugin.get_features = _get_features; + } + + return &this->public.plugin; +} diff --git a/src/libstrongswan/plugins/rdrand/rdrand_plugin.h b/src/libstrongswan/plugins/rdrand/rdrand_plugin.h new file mode 100644 index 000000000..6f0e55313 --- /dev/null +++ b/src/libstrongswan/plugins/rdrand/rdrand_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * 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. + */ + +/** + * @defgroup rdrand_p rdrand + * @ingroup plugins + * + * @defgroup rdrand_plugin rdrand_plugin + * @{ @ingroup rdrand_p + */ + +#ifndef RDRAND_PLUGIN_H_ +#define RDRAND_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct rdrand_plugin_t rdrand_plugin_t; + +/** + * Plugin providing random generators based on Intels RDRAND instruction. + */ +struct rdrand_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** RDRAND_PLUGIN_H_ @}*/ diff --git a/src/libstrongswan/plugins/rdrand/rdrand_rng.c b/src/libstrongswan/plugins/rdrand/rdrand_rng.c new file mode 100644 index 000000000..d032cbe31 --- /dev/null +++ b/src/libstrongswan/plugins/rdrand/rdrand_rng.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * 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 "rdrand_rng.h" + +#include <unistd.h> + +typedef struct private_rdrand_rng_t private_rdrand_rng_t; + +/** + * Private data of an rdrand_rng_t object. + */ +struct private_rdrand_rng_t { + + /** + * Public rdrand_rng_t interface. + */ + rdrand_rng_t public; + + /** + * Quality we produce RNG data + */ + rng_quality_t quality; +}; + +/** + * Retries for failed RDRAND instructions + */ +#define MAX_TRIES 16 + +/** + * After how many bytes should we reseed for RNG_STRONG + * (must be a power of two >= 8) + */ +#define FORCE_RESEED 16 + +/** + * How many times we mix reseeded RDRAND output when using RNG_TRUE + */ +#define MIX_ROUNDS 32 + +/** + * Get a two byte word using RDRAND + */ +static bool rdrand16(u_int16_t *out) +{ + u_char res; + int i; + + for (i = 0; i < MAX_TRIES; i++) + { + asm(".byte 0x66;.byte 0x0f;.byte 0xc7;.byte 0xf0; " /* rdrand */ + "setc %1;" + : "=a"(*out), "=qm"(res)); + + if (res) + { + return TRUE; + } + } + return FALSE; +} + +/** + * Get a four byte word using RDRAND + */ +static bool rdrand32(u_int32_t *out) +{ + u_char res; + int i; + + for (i = 0; i < MAX_TRIES; i++) + { + asm(".byte 0x0f;.byte 0xc7;.byte 0xf0;" /* rdrand */ + "setc %1;" + : "=a"(*out), "=qm"(res)); + + if (res) + { + return TRUE; + } + } + return FALSE; +} + +#ifdef __x86_64__ +/** + * Get a eight byte word using RDRAND + */ +static bool rdrand64(u_int64_t *out) +{ + u_char res; + int i; + + for (i = 0; i < MAX_TRIES; i++) + { + asm(".byte 0x48;.byte 0x0f;.byte 0xc7;.byte 0xf0;" /* rdrand */ + "setc %1;" + : "=a"(*out), "=qm"(res)); + + if (res) + { + return TRUE; + } + } + return FALSE; +} +#endif /* __x86_64__ */ + +/** + * Get a one byte word using RDRAND + */ +static bool rdrand8(u_int8_t *out) +{ + u_int16_t u16; + + if (!rdrand16(&u16)) + { + return FALSE; + } + *out = u16; + return TRUE; +} + +/** + * Get a 16 byte word using RDRAND + */ +static bool rdrand128(void *out) +{ +#ifdef __x86_64__ + if (!rdrand64(out) || + !rdrand64(out + sizeof(u_int64_t))) + { + return FALSE; + } +#else /* __i386__ */ + if (!rdrand32(out) || + !rdrand32(out + 1 * sizeof(u_int32_t)) || + !rdrand32(out + 2 * sizeof(u_int32_t)) || + !rdrand32(out + 3 * sizeof(u_int32_t))) + { + return FALSE; + } +#endif /* __x86_64__ / __i386__ */ + return TRUE; +} + +/** + * Enforce a DRNG reseed by reading 511 128-bit samples + */ +static bool reseed() +{ + int i; + +#ifdef __x86_64__ + u_int64_t tmp; + + for (i = 0; i < 511 * 16 / sizeof(u_int64_t); i++) + { + if (!rdrand64(&tmp)) + { + return FALSE; + } + } +#else /* __i386__ */ + u_int32_t tmp; + + for (i = 0; i < 511 * 16 / sizeof(u_int32_t); i++) + { + if (!rdrand32(&tmp)) + { + return FALSE; + } + } +#endif /* __x86_64__ / __i386__ */ + return TRUE; +} + +/** + * Fill a preallocated chunk of data with random bytes + */ +static bool rdrand_chunk(private_rdrand_rng_t *this, chunk_t chunk) +{ + if (this->quality == RNG_STRONG) + { + if (!reseed()) + { + return FALSE; + } + } + + /* align to 2 byte */ + if (chunk.len >= sizeof(u_int8_t)) + { + if ((uintptr_t)chunk.ptr % 2) + { + if (!rdrand8((u_int8_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int8_t)); + } + } + + /* align to 4 byte */ + if (chunk.len >= sizeof(u_int16_t)) + { + if ((uintptr_t)chunk.ptr % 4) + { + if (!rdrand16((u_int16_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int16_t)); + } + } + +#ifdef __x86_64__ + + /* align to 8 byte */ + if (chunk.len >= sizeof(u_int32_t)) + { + if ((uintptr_t)chunk.ptr % 8) + { + if (!rdrand32((u_int32_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int32_t)); + } + } + + /* fill with 8 byte words */ + while (chunk.len >= sizeof(u_int64_t)) + { + if (this->quality == RNG_STRONG && chunk.len % FORCE_RESEED) + { + if (!reseed()) + { + return FALSE; + } + } + if (!rdrand64((u_int64_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int64_t)); + } + + /* append 4 byte word */ + if (chunk.len >= sizeof(u_int32_t)) + { + if (!rdrand32((u_int32_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int32_t)); + } + +#else /* __i386__ */ + + /* fill with 4 byte words */ + while (chunk.len >= sizeof(u_int32_t)) + { + if (this->quality == RNG_STRONG && chunk.len % FORCE_RESEED) + { + if (!reseed()) + { + return FALSE; + } + } + if (!rdrand32((u_int32_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int32_t)); + } + +#endif /* __x86_64__ / __i386__ */ + + if (this->quality == RNG_STRONG) + { + if (!reseed()) + { + return FALSE; + } + } + + /* append 2 byte word */ + if (chunk.len >= sizeof(u_int16_t)) + { + if (!rdrand16((u_int16_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int16_t)); + } + + /* append 1 byte word */ + if (chunk.len >= sizeof(u_int8_t)) + { + if (!rdrand8((u_int8_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int8_t)); + } + + return TRUE; +} + +/** + * Stronger variant mixing reseeded results of rdrand output + * + * This is based on the Intel DRNG "Software Implementation Guide", using + * AES-CBC to mix several reseeded RDRAND outputs. + */ +static bool rdrand_mixed(private_rdrand_rng_t *this, chunk_t chunk) +{ + u_char block[16], forward[16], key[16], iv[16]; + crypter_t *crypter; + int i, len; + + memset(iv, 0, sizeof(iv)); + crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CBC, 16); + if (!crypter) + { + return FALSE; + } + for (i = 0; i < sizeof(key); i++) + { + key[i] = i; + } + if (!crypter->set_key(crypter, chunk_from_thing(key))) + { + crypter->destroy(crypter); + return FALSE; + } + while (chunk.len > 0) + { + memset(forward, 0, sizeof(forward)); + for (i = 0; i < MIX_ROUNDS; i++) + { + /* sleep to reseed PRNG */ + usleep(10); + if (!rdrand128(block)) + { + crypter->destroy(crypter); + return FALSE; + } + memxor(forward, block, sizeof(block)); + if (!crypter->encrypt(crypter, chunk_from_thing(forward), + chunk_from_thing(iv), NULL)) + { + crypter->destroy(crypter); + return FALSE; + } + } + len = min(chunk.len, sizeof(forward)); + memcpy(chunk.ptr, forward, len); + chunk = chunk_skip(chunk, len); + } + crypter->destroy(crypter); + + return TRUE; +} + +METHOD(rng_t, get_bytes, bool, + private_rdrand_rng_t *this, size_t bytes, u_int8_t *buffer) +{ + switch (this->quality) + { + case RNG_WEAK: + case RNG_STRONG: + return rdrand_chunk(this, chunk_create(buffer, bytes)); + case RNG_TRUE: + return rdrand_mixed(this, chunk_create(buffer, bytes)); + default: + return FALSE; + } +} + +METHOD(rng_t, allocate_bytes, bool, + private_rdrand_rng_t *this, size_t bytes, chunk_t *chunk) +{ + *chunk = chunk_alloc(bytes); + if (get_bytes(this, bytes, chunk->ptr)) + { + return TRUE; + } + free(chunk->ptr); + return FALSE; +} + +METHOD(rng_t, destroy, void, + private_rdrand_rng_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +rdrand_rng_t *rdrand_rng_create(rng_quality_t quality) +{ + private_rdrand_rng_t *this; + + switch (quality) + { + case RNG_WEAK: + case RNG_STRONG: + case RNG_TRUE: + break; + default: + return NULL; + } + + INIT(this, + .public = { + .rng = { + .get_bytes = _get_bytes, + .allocate_bytes = _allocate_bytes, + .destroy = _destroy, + }, + }, + .quality = quality, + ); + + return &this->public; +} diff --git a/src/libstrongswan/plugins/rdrand/rdrand_rng.h b/src/libstrongswan/plugins/rdrand/rdrand_rng.h new file mode 100644 index 000000000..d15a48224 --- /dev/null +++ b/src/libstrongswan/plugins/rdrand/rdrand_rng.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * 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. + */ + +/** + * @defgroup rdrand_rng rdrand_rng + * @{ @ingroup rdrand + */ + +#ifndef RDRAND_RNG_H_ +#define RDRAND_RNG_H_ + +#include <crypto/rngs/rng.h> + +typedef struct rdrand_rng_t rdrand_rng_t; + +/** + * RNG implemented with Intels RDRAND instructions, introduced in Ivy Bridge. + */ +struct rdrand_rng_t { + + /** + * Implements rng_t interface. + */ + rng_t rng; +}; + +/** + * Create a rdrand_rng instance. + * + * @param quality RNG quality + * @return RNG instance + */ +rdrand_rng_t *rdrand_rng_create(rng_quality_t quality); + +#endif /** RDRAND_RNG_H_ @}*/ |