[PATCH v2] ell: add missing include in dhcp-server.
by Érico Nogueira
This is v2 only because I'm attempting to send it again.
Hope it works!
ell/dhcp-server.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
index c983ed4..144e83e 100644
--- a/ell/dhcp-server.c
+++ b/ell/dhcp-server.c
@@ -24,6 +24,7 @@
#include <config.h>
#endif
+#include <linux/types.h>
#include <net/ethernet.h>
#include <sys/socket.h>
#include <arpa/inet.h>
--
2.29.2
1 year, 8 months
[PATCH] ell: add missing include in dhcp-server.
by Érico Nogueira
From: Érico Rolim <erico.erc(a)gmail.com>
---
I would prefer adding `#include <linux/types>` directly in
dhcp-private.h, but this patch follows the current style.
ell/dhcp-server.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
index c983ed4..144e83e 100644
--- a/ell/dhcp-server.c
+++ b/ell/dhcp-server.c
@@ -24,6 +24,7 @@
#include <config.h>
#endif
+#include <linux/types.h>
#include <net/ethernet.h>
#include <sys/socket.h>
#include <arpa/inet.h>
--
2.29.2
1 year, 8 months
[BUG] ELL doesn't have a fallback for rawmemchr
by Érico Nogueira
Hi!
Building ELL 0.35 on a musl system fails, due to ell/pem.c using
rawmemchr(), which is a glibc extension. Building IWD with internal
ELL fails for the same reason.
I see that IWD itself has src/missing.h with a fallback, but ELL lacks
that.
We are using the following patch to fix the build:
diff --git a/ell/pem.c b/ell/pem.c
index 790f2c2..237ae02 100644
--- a/ell/pem.c
+++ b/ell/pem.c
@@ -224,7 +224,7 @@ static uint8_t *pem_load_buffer(const void *buf, size_t=
buf_len,
=20
/* Check that each header line has a key and a colon */
while (start < end) {
- const char *lf =3D rawmemchr(start, '\n');
+ const char *lf =3D memchr(start, '\n', end - start);
const char *colon =3D memchr(start, ':', lf - start);
=20
if (!colon)
--
If ELL is willing to include a src/missing.h of their own, that would
solve the issue, but avoiding rawmemchr() completely is also a
possibility.
Thanks,
Érico
1 year, 8 months
[PATCH 0/2] Fix "NLA_F_NESTED is missing" error
by Ossama Othman
This set of patches addresses a "NLA_F_NESTED is missing" error under
strict nested netlink attribute validation by the kernel. Users can
work around the error by explicitly setting the NLA_F_NESTED bit in
the type passed to l_genl_msg_enter_nested(), but having ELL
transparently set the bit seems more appropriate.
Ossama Othman (2):
genl: Add missing NLA_F_NESTED bit
unit: Update expected nested netlink attr data
ell/genl.c | 2 +-
unit/test-genl-msg.c | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
--
2.25.1
1 year, 8 months
[PATCH v3 1/5] dhcp-server: add timer for tracking expired leases
by James Prestwood
Currently dhcp-server has a limitation where it only calls the
lease expired callback if a client actually releases the lease
itself.
This patchs adds a single timer to the server object which tracks
the soonest expiring lease. When this lease expires the event is
triggered and the timeout is reset to the (new) soonest expiring
lease.
---
ell/dhcp-server.c | 160 ++++++++++++++++++++++++++++++++++++----------
1 file changed, 128 insertions(+), 32 deletions(-)
v3:
* Reworked to avoid needed a separate struct for server/lease pairs
* Moved nearly all timer/add/remove logic into get_lease. This now
handles (re)new leases, expired leases, and released leases and
sets the timer as needed.
diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
index fd38c66..ac711b9 100644
--- a/ell/dhcp-server.c
+++ b/ell/dhcp-server.c
@@ -37,6 +37,7 @@
#include "queue.h"
#include "util.h"
#include "strv.h"
+#include "timeout.h"
/* 8 hours */
#define DEFAULT_DHCP_LEASE_SEC (8*60*60)
@@ -44,6 +45,8 @@
/* 5 minutes */
#define OFFER_TIME (5*60)
+#define MAX_EXPIRED_LEASES 50
+
static const uint8_t MAC_BCAST_ADDR[ETH_ALEN] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
@@ -59,8 +62,13 @@ struct l_dhcp_server {
uint32_t gateway;
uint32_t *dns_list;
uint32_t lease_seconds;
+ unsigned int max_expired;
struct l_queue *lease_list;
+ struct l_queue *expired_list;
+
+ /* Next lease expiring */
+ struct l_timeout *next_expire;
l_dhcp_debug_cb_t debug_handler;
void *debug_data;
@@ -156,16 +164,75 @@ static int get_lease(struct l_dhcp_server *server, uint32_t yiaddr,
return 0;
}
-static int compare_lifetime(const void *a, const void *b, void *user_data)
+static int compare_lifetime_or_offering(const void *a, const void *b,
+ void *user_data)
{
const struct l_dhcp_lease *lease1 = a;
const struct l_dhcp_lease *lease2 = b;
+ /*
+ * Ensures offered but not active leases stay at the head of the queue.
+ * This lets us peek at the tail to find the next expiring (active)
+ */
+ if (lease1->offering)
+ return 1;
+
return lease2->lifetime - lease1->lifetime;
}
+static void lease_expired_cb(struct l_timeout *timeout, void *user_data);
+
+static void set_next_expire_timer(struct l_dhcp_server *server,
+ struct l_dhcp_lease *expired)
+{
+ struct l_dhcp_lease *next;
+ unsigned int next_timeout;
+
+ /*
+ * If this is an expiring lease put it into the expired queue, removing
+ * a lease if we have reached the max
+ */
+ if (expired) {
+ l_queue_remove(server->lease_list, expired);
+
+ if (l_queue_length(server->expired_list) > server->max_expired)
+ _dhcp_lease_free(l_queue_pop_head(
+ server->expired_list));
+
+ l_queue_push_tail(server->expired_list, expired);
+ }
+
+ next = l_queue_peek_tail(server->lease_list);
+ if (!next || next->offering) {
+ server->next_expire = NULL;
+ return;
+ }
+
+ next_timeout = l_time_to_secs(l_time_diff(l_time_now(),
+ next->lifetime));
+
+ if (server->next_expire)
+ l_timeout_modify(server->next_expire, next_timeout);
+ else
+ server->next_expire = l_timeout_create(server->lease_seconds,
+ lease_expired_cb,
+ server, NULL);
+}
+
+static void lease_expired_cb(struct l_timeout *timeout, void *user_data)
+{
+ struct l_dhcp_server *server = user_data;
+ struct l_dhcp_lease *lease = l_queue_peek_tail(server->expired_list);
+
+ if (server->event_handler)
+ server->event_handler(server, L_DHCP_SERVER_EVENT_LEASE_EXPIRED,
+ server->user_data, lease);
+
+ set_next_expire_timer(server, lease);
+}
+
static struct l_dhcp_lease *add_lease(struct l_dhcp_server *server,
- uint32_t expire, const uint8_t *chaddr,
+ bool offering, const uint8_t *chaddr,
uint32_t yiaddr)
{
struct l_dhcp_lease *lease = NULL;
@@ -180,13 +247,28 @@ static struct l_dhcp_lease *add_lease(struct l_dhcp_server *server,
memcpy(lease->mac, chaddr, ETH_ALEN);
lease->address = yiaddr;
- if (expire == 0)
- lease->lifetime = l_time_to_secs(l_time_now()) +
- server->lease_seconds;
- else
- lease->lifetime = expire;
-
- l_queue_insert(server->lease_list, lease, compare_lifetime, NULL);
+ lease->offering = offering;
+ lease->lifetime = l_time_to_secs(l_time_now());
+
+ if (!offering) {
+ lease->lifetime += server->lease_seconds;
+
+ /*
+ * Insert into queue by lifetime (skipping any offered leases
+ * at the head)
+ */
+ l_queue_insert(server->lease_list, lease,
+ compare_lifetime_or_offering, NULL);
+ /*
+ * This is a new (or renewed lease) so pass NULL for expired so
+ * the queue's are not modified, only the next_expired timer
+ */
+ set_next_expire_timer(server, NULL);
+ } else {
+ lease->lifetime += OFFER_TIME;
+ /* Push offered leases to head, active leases after those */
+ l_queue_push_head(server->lease_list, lease);
+ }
SERVER_DEBUG("added lease IP %s for "MAC " lifetime=%u",
IP_STR(yiaddr), MAC_STR(chaddr),
@@ -196,17 +278,21 @@ static struct l_dhcp_lease *add_lease(struct l_dhcp_server *server,
}
static void lease_release(struct l_dhcp_server *server,
- struct l_dhcp_lease *lease, uint32_t expire)
+ struct l_dhcp_lease *lease)
{
- l_queue_remove(server->lease_list, lease);
-
- lease->lifetime = expire;
-
- l_queue_insert(server->lease_list, lease, compare_lifetime, NULL);
+ /*
+ * If the client released the lease after the server timeout expired
+ * there is nothing to do. Otherwise the client is releasing the
+ * lease early which may require re-setting the lease expire timer
+ */
+ if (is_expired_lease(lease))
+ return;
if (server->event_handler)
server->event_handler(server, L_DHCP_SERVER_EVENT_LEASE_EXPIRED,
server->user_data, lease);
+
+ set_next_expire_timer(server, lease);
}
static bool match_lease_ip(const void *data, const void *user_data)
@@ -216,11 +302,10 @@ static bool match_lease_ip(const void *data, const void *user_data)
return lease->address == L_PTR_TO_UINT(user_data);
}
-static struct l_dhcp_lease *find_lease_by_ip(struct l_dhcp_server *server,
+static struct l_dhcp_lease *find_lease_by_ip(struct l_queue *lease_list,
uint32_t nip)
{
- return l_queue_find(server->lease_list, match_lease_ip,
- L_UINT_TO_PTR(nip));
+ return l_queue_find(lease_list, match_lease_ip, L_UINT_TO_PTR(nip));
}
static bool check_requested_ip(struct l_dhcp_server *server,
@@ -237,7 +322,7 @@ static bool check_requested_ip(struct l_dhcp_server *server,
if (htonl(requested_nip) > server->end_ip)
return false;
- lease = find_lease_by_ip(server, requested_nip);
+ lease = find_lease_by_ip(server->lease_list, requested_nip);
if (!lease)
return true;
@@ -272,7 +357,17 @@ static uint32_t find_free_or_expired_ip(struct l_dhcp_server *server,
if ((ip_addr & 0xff) == 0xff)
continue;
- lease = find_lease_by_ip(server, ip_nl);
+ /*
+ * Search both active and expired leases. If this exausts all
+ * IP's in the range pop the expired list (oldest expired lease)
+ * and use that IP. If the expired list is empty we have reached
+ * our maximum number of clients.
+ */
+ lease = find_lease_by_ip(server->lease_list, ip_nl);
+ if (lease)
+ continue;
+
+ lease = find_lease_by_ip(server->expired_list, ip_nl);
if (lease)
continue;
@@ -280,16 +375,10 @@ static uint32_t find_free_or_expired_ip(struct l_dhcp_server *server,
return ip_nl;
}
- lease = l_queue_peek_tail(server->lease_list);
+ lease = l_queue_pop_head(server->expired_list);
if (!lease)
return 0;
- if (!is_expired_lease(lease))
- return 0;
-
- if (!arp_check(lease->address, safe_mac))
- return 0;
-
return lease->address;
}
@@ -350,8 +439,7 @@ static void send_offer(struct l_dhcp_server *server,
return;
}
- lease = add_lease(server, l_time_to_secs(l_time_now()) + OFFER_TIME,
- client_msg->chaddr, reply->yiaddr);
+ lease = add_lease(server, true, client_msg->chaddr, reply->yiaddr);
if (!lease) {
SERVER_DEBUG("No free IP addresses, OFFER abandoned");
return;
@@ -467,7 +555,7 @@ static void send_ack(struct l_dhcp_server *server,
return;
}
- lease = add_lease(server, 0, reply->chaddr, reply->yiaddr);
+ lease = add_lease(server, false, reply->chaddr, reply->yiaddr);
if (server->event_handler)
server->event_handler(server, L_DHCP_SERVER_EVENT_NEW_LEASE,
@@ -567,8 +655,7 @@ static void listener_event(const void *data, size_t len, void *user_data)
break;
if (message->ciaddr == lease->address)
- lease_release(server, lease,
- l_time_to_secs(l_time_now()));
+ lease_release(server, lease);
break;
case DHCP_MESSAGE_TYPE_INFORM:
@@ -607,10 +694,12 @@ LIB_EXPORT struct l_dhcp_server *l_dhcp_server_new(int ifindex)
return NULL;
server->lease_list = l_queue_new();
+ server->expired_list = l_queue_new();
server->started = false;
server->lease_seconds = DEFAULT_DHCP_LEASE_SEC;
+ server->max_expired = MAX_EXPIRED_LEASES;
server->ifindex = ifindex;
server->debug_handler = NULL;
@@ -634,6 +723,8 @@ LIB_EXPORT void l_dhcp_server_destroy(struct l_dhcp_server *server)
l_queue_destroy(server->lease_list,
(l_queue_destroy_func_t) _dhcp_lease_free);
+ l_queue_destroy(server->expired_list,
+ (l_queue_destroy_func_t) _dhcp_lease_free);
if (server->dns_list)
l_free(server->dns_list);
@@ -720,6 +811,11 @@ LIB_EXPORT bool l_dhcp_server_stop(struct l_dhcp_server *server)
server->started = false;
+ if (server->next_expire) {
+ l_timeout_remove(server->next_expire);
+ server->next_expire = NULL;
+ }
+
/* TODO: Add ability to save leases */
return true;
--
2.26.2
1 year, 8 months
[PATCH 1/5] pem: Parse PKCS#1 formatted private keys
by Andrew Zaborowski
The PKCS#1 RSAPrivateKey format is used within the PKCS#8 PrivateKey
structure so we just need to wrap it in some extra ASN.1 data to get a
format understood by the pkcs8-key-parse module.
Improve some comments with the right RFC references.
---
ell/asn1-private.h | 1 +
ell/pem.c | 106 +++++++++++++++++++++++++++++++++++++++++----
2 files changed, 98 insertions(+), 9 deletions(-)
diff --git a/ell/asn1-private.h b/ell/asn1-private.h
index e34f977..2a31241 100644
--- a/ell/asn1-private.h
+++ b/ell/asn1-private.h
@@ -29,6 +29,7 @@
#define ASN1_ID_INTEGER ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x02)
#define ASN1_ID_BIT_STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x03)
#define ASN1_ID_OCTET_STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x04)
+#define ASN1_ID_NULL ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x05)
#define ASN1_ID_OID ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x06)
#define ASN1_ID_UTF8STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x0c)
#define ASN1_ID_PRINTABLESTRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x13)
diff --git a/ell/pem.c b/ell/pem.c
index b54270e..99020b1 100644
--- a/ell/pem.c
+++ b/ell/pem.c
@@ -400,14 +400,21 @@ static struct l_key *pem_load_private_key(uint8_t *content,
struct l_key *pkey = NULL;
/*
- * RFC7469- and PKCS#8-compatible label (default in OpenSSL 1.0.1+)
- * and the older (OpenSSL <= 0.9.8 default) label.
+ * RFC7468 Section 10-compatible unencrypted private key label
+ * (also mentioned in PKCS#8/RFC5958 Section 5), encodes
+ * the PKCS#8/RFC5958 PrivateKeyInfo structure -- supported
+ * directly by the pkcs8-key-parser kernel module.
*/
- if (!strcmp(label, "PRIVATE KEY") ||
- !strcmp(label, "RSA PRIVATE KEY"))
+ if (!strcmp(label, "PRIVATE KEY"))
goto done;
- /* RFC5958 (PKCS#8) section 3 type encrypted key label */
+ /*
+ * RFC7468 Section 11-compatible encrypted private key label
+ * (also mentioned in PKCS#8/RFC5958 Section 5), encodes
+ * the PKCS#8/RFC5958 EncryptedPrivateKeyInfo structure. We
+ * decrypt it into a plain PrivateKeyInfo for the
+ * pkcs8-key-parser module.
+ */
if (!strcmp(label, "ENCRYPTED PRIVATE KEY")) {
const uint8_t *key_info, *alg_id, *data;
uint8_t tag;
@@ -479,11 +486,92 @@ static struct l_key *pem_load_private_key(uint8_t *content,
}
/*
- * TODO: handle RSA PRIVATE KEY format encrypted keys
- * (as produced by "openssl rsa" commands), incompatible with
- * RFC7468 parsing because of the headers present before
- * base64-encoded data.
+ * Legacy RSA private key label aka. SSLeay format label, created
+ * by most software but not documented in an RFC. Encodes the
+ * PKCS#1/RFC8017 RSAPrivateKey structure. We wrap it in a PKCS#8
+ * PrivateKeyInfo for the pkcs8-key-parser module.
+ *
+ * TODO: decrypt RSA PRIVATE KEY format encrypted keys
+ * as produced by "openssl rsa" commands. These are incompatible
+ * with RFC7468 parsing because of the RFC822 headers present
+ * before base64-encoded data. The format is documented in
+ * RFC1421 and the encryption algorithms in RFC1423 although
+ * openssl allows many more algorithms than those documented.
+ * Then wrap in a PrivateKeyInfo like above.
*/
+ if (!strcmp(label, "RSA PRIVATE KEY")) {
+ const uint8_t *data;
+ uint8_t tag;
+ size_t data_len;
+ const uint8_t *key_data;
+ size_t key_data_len;
+ int i;
+ uint8_t *private_key;
+ size_t private_key_len;
+ uint8_t *one_asymmetric_key;
+ uint8_t *ptr;
+
+ static const uint8_t version0[] = {
+ ASN1_ID_INTEGER, 0x01, 0x00
+ };
+ static const uint8_t pkcs1_rsa_encryption[] = {
+ ASN1_ID_SEQUENCE, 0x0d,
+ ASN1_ID_OID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x01,
+ ASN1_ID_NULL, 0x00,
+ };
+
+ /*
+ * Sanity check that it's a version 0 or 1 RSAPrivateKey
+ * structure with the 8 integers, if it's not, make a last
+ * ditch attempt to load it into the kernel directly.
+ */
+ key_data = asn1_der_find_elem(content, len, 0, &tag,
+ &key_data_len);
+ if (!key_data || tag != ASN1_ID_SEQUENCE)
+ goto done;
+
+ data = asn1_der_find_elem(key_data, key_data_len, 0, &tag,
+ &data_len);
+ if (!data || tag != ASN1_ID_INTEGER || data_len != 1 ||
+ (data[0] != 0x00 && data[0] != 0x01))
+ goto done;
+
+ for (i = 1; i < 9; i++) {
+ data = asn1_der_find_elem(key_data, key_data_len,
+ i, &tag, &data_len);
+ if (!data || tag != ASN1_ID_INTEGER || data_len < 1)
+ goto done;
+ }
+
+ private_key = l_malloc(10 + len);
+ ptr = private_key;
+ *ptr++ = ASN1_ID_OCTET_STRING;
+ asn1_write_definite_length(&ptr, len);
+ memcpy(ptr, content, len);
+ ptr += len;
+ private_key_len = ptr - private_key;
+
+ one_asymmetric_key = l_malloc(32 + private_key_len);
+ ptr = one_asymmetric_key;
+ *ptr++ = ASN1_ID_SEQUENCE;
+ asn1_write_definite_length(&ptr,
+ sizeof(version0) +
+ sizeof(pkcs1_rsa_encryption) +
+ private_key_len);
+ memcpy(ptr, version0, sizeof(version0));
+ ptr += sizeof(version0);
+ memcpy(ptr, pkcs1_rsa_encryption, sizeof(pkcs1_rsa_encryption));
+ ptr += sizeof(pkcs1_rsa_encryption);
+ memcpy(ptr, private_key, private_key_len);
+ ptr += private_key_len;
+ l_free(private_key);
+
+ l_free(content);
+ content = one_asymmetric_key;
+ len = ptr - one_asymmetric_key;
+ goto done;
+ }
/* Label not known */
goto err;
--
2.27.0
1 year, 8 months
[PATCH v2 1/9] dhcp: release lease when client stops
by James Prestwood
RFC 2131 does not explicitly require this but clients MAY
release their IP address when they are done using it, e.g.
when shutting down. This is also described as a "graceful shutdown"
so its probably best the DHCP client does this.
---
ell/dhcp.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 65 insertions(+), 9 deletions(-)
v2:
* Send release in BOUND, RENEW, and REBINDING states
diff --git a/ell/dhcp.c b/ell/dhcp.c
index e6fbda7..dc4e73b 100644
--- a/ell/dhcp.c
+++ b/ell/dhcp.c
@@ -367,6 +367,19 @@ static int dhcp_client_send_discover(struct l_dhcp_client *client)
discover, len);
}
+static int dhcp_client_send_unicast(struct l_dhcp_client *client,
+ struct dhcp_message *request,
+ unsigned int len)
+{
+ struct sockaddr_in si;
+
+ memset(&si, 0, sizeof(si));
+ si.sin_family = AF_INET;
+ si.sin_port = L_CPU_TO_BE16(DHCP_PORT_SERVER);
+ si.sin_addr.s_addr = client->lease->server_address;
+ return client->transport->send(client->transport, &si, request, len);
+}
+
static int dhcp_client_send_request(struct l_dhcp_client *client)
{
struct dhcp_message_builder builder;
@@ -450,15 +463,8 @@ static int dhcp_client_send_request(struct l_dhcp_client *client)
* 'server identifier' option for any unicast requests to the DHCP
* server.
*/
- if (client->state == DHCP_STATE_RENEWING) {
- struct sockaddr_in si;
- memset(&si, 0, sizeof(si));
- si.sin_family = AF_INET;
- si.sin_port = L_CPU_TO_BE16(DHCP_PORT_SERVER);
- si.sin_addr.s_addr = client->lease->server_address;
- return client->transport->send(client->transport,
- &si, request, len);
- }
+ if (client->state == DHCP_STATE_RENEWING)
+ return dhcp_client_send_unicast(client, request, len);
return client->transport->l2_send(client->transport,
INADDR_ANY, DHCP_PORT_CLIENT,
@@ -466,6 +472,45 @@ static int dhcp_client_send_request(struct l_dhcp_client *client)
NULL, request, len);
}
+static void dhcp_client_send_release(struct l_dhcp_client *client)
+{
+ struct dhcp_message_builder builder;
+ size_t optlen = DHCP_MIN_OPTIONS_SIZE;
+ size_t len = sizeof(struct dhcp_message) + optlen;
+ L_AUTO_FREE_VAR(struct dhcp_message *, request);
+ int err;
+ struct sockaddr_in si;
+
+ CLIENT_DEBUG("");
+
+ memset(&si, 0, sizeof(si));
+ si.sin_family = AF_INET;
+ si.sin_port = L_CPU_TO_BE16(DHCP_PORT_SERVER);
+ si.sin_addr.s_addr = client->lease->server_address;
+
+ request = (struct dhcp_message *) l_new(uint8_t, len);
+
+ _dhcp_message_builder_init(&builder, request, len,
+ DHCP_MESSAGE_TYPE_RELEASE);
+
+ err = client_message_init(client, request, &builder);
+ if (err < 0)
+ return;
+
+ request->ciaddr = client->lease->address;
+
+ if (!_dhcp_message_builder_append(&builder,
+ L_DHCP_OPTION_SERVER_IDENTIFIER,
+ 4, &client->lease->server_address)) {
+ CLIENT_DEBUG("Failed to append server ID");
+ return;
+ }
+
+ _dhcp_message_builder_finalize(&builder, &len);
+
+ dhcp_client_send_unicast(client, request, len);
+}
+
static void dhcp_client_timeout_resend(struct l_timeout *timeout,
void *user_data)
{
@@ -1063,6 +1108,17 @@ LIB_EXPORT bool l_dhcp_client_stop(struct l_dhcp_client *client)
if (unlikely(!client))
return false;
+ /*
+ * RFC 2131 Section 4.4.6
+ * "If the client no longer requires use of its assigned network address
+ * (e.g., the client is gracefully shut down), the client sends a
+ * DHCPRELEASE message to the server.""
+ */
+ if (client->state == DHCP_STATE_BOUND ||
+ client->state == DHCP_STATE_RENEWING ||
+ client->state == DHCP_STATE_REBINDING)
+ dhcp_client_send_release(client);
+
if (client->rtnl_add_cmdid) {
l_netlink_cancel(client->rtnl, client->rtnl_add_cmdid);
client->rtnl_add_cmdid = 0;
--
2.26.2
1 year, 8 months
ctype.h undefined behaviour on signed char platforms, needs cast to
(unsigned char)
by Phil
Just a little reminder that a cast to (unsigned char) is needed when passing a possibly negative char to ctype functions like isalnum, isspace. Passing anything other than positive or -1 is undefined behaviour, on platforms where char is signed.
One way to find them all is to build from time to time with ctype.h include commented out. Static review tools may also help.
+#include <ctype.h>
+ while (base64_len && isspace(*base64)) {
1 year, 8 months
[PATCH 1/5] pem: Parse PKCS#1 formatted private keys
by Andrew Zaborowski
The PKCS#1 RSAPrivateKey format is used within the PKCS#8 PrivateKey
structure so we just need to wrap it in some extra ASN.1 data to get a
format understood by the pkcs8-key-parse module.
Improve some comments with the right RFC references.
---
ell/asn1-private.h | 1 +
ell/pem.c | 106 +++++++++++++++++++++++++++++++++++++++++----
2 files changed, 98 insertions(+), 9 deletions(-)
diff --git a/ell/asn1-private.h b/ell/asn1-private.h
index e34f977..2a31241 100644
--- a/ell/asn1-private.h
+++ b/ell/asn1-private.h
@@ -29,6 +29,7 @@
#define ASN1_ID_INTEGER ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x02)
#define ASN1_ID_BIT_STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x03)
#define ASN1_ID_OCTET_STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x04)
+#define ASN1_ID_NULL ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x05)
#define ASN1_ID_OID ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x06)
#define ASN1_ID_UTF8STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x0c)
#define ASN1_ID_PRINTABLESTRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x13)
diff --git a/ell/pem.c b/ell/pem.c
index b54270e..99020b1 100644
--- a/ell/pem.c
+++ b/ell/pem.c
@@ -400,14 +400,21 @@ static struct l_key *pem_load_private_key(uint8_t *content,
struct l_key *pkey = NULL;
/*
- * RFC7469- and PKCS#8-compatible label (default in OpenSSL 1.0.1+)
- * and the older (OpenSSL <= 0.9.8 default) label.
+ * RFC7468 Section 10-compatible unencrypted private key label
+ * (also mentioned in PKCS#8/RFC5958 Section 5), encodes
+ * the PKCS#8/RFC5958 PrivateKeyInfo structure -- supported
+ * directly by the pkcs8-key-parser kernel module.
*/
- if (!strcmp(label, "PRIVATE KEY") ||
- !strcmp(label, "RSA PRIVATE KEY"))
+ if (!strcmp(label, "PRIVATE KEY"))
goto done;
- /* RFC5958 (PKCS#8) section 3 type encrypted key label */
+ /*
+ * RFC7468 Section 11-compatible encrypted private key label
+ * (also mentioned in PKCS#8/RFC5958 Section 5), encodes
+ * the PKCS#8/RFC5958 EncryptedPrivateKeyInfo structure. We
+ * decrypt it into a plain PrivateKeyInfo for the
+ * pkcs8-key-parser module.
+ */
if (!strcmp(label, "ENCRYPTED PRIVATE KEY")) {
const uint8_t *key_info, *alg_id, *data;
uint8_t tag;
@@ -479,11 +486,92 @@ static struct l_key *pem_load_private_key(uint8_t *content,
}
/*
- * TODO: handle RSA PRIVATE KEY format encrypted keys
- * (as produced by "openssl rsa" commands), incompatible with
- * RFC7468 parsing because of the headers present before
- * base64-encoded data.
+ * Legacy RSA private key label aka. SSLeay format label, created
+ * by most software but not documented in an RFC. Encodes the
+ * PKCS#1/RFC8017 RSAPrivateKey structure. We wrap it in a PKCS#8
+ * PrivateKeyInfo for the pkcs8-key-parser module.
+ *
+ * TODO: decrypt RSA PRIVATE KEY format encrypted keys
+ * as produced by "openssl rsa" commands. These are incompatible
+ * with RFC7468 parsing because of the RFC822 headers present
+ * before base64-encoded data. The format is documented in
+ * RFC1421 and the encryption algorithms in RFC1423 although
+ * openssl allows many more algorithms than those documented.
+ * Then wrap in a PrivateKeyInfo like above.
*/
+ if (!strcmp(label, "RSA PRIVATE KEY")) {
+ const uint8_t *data;
+ uint8_t tag;
+ size_t data_len;
+ const uint8_t *key_data;
+ size_t key_data_len;
+ int i;
+ uint8_t *private_key;
+ size_t private_key_len;
+ uint8_t *one_asymmetric_key;
+ uint8_t *ptr;
+
+ static const uint8_t version0[] = {
+ ASN1_ID_INTEGER, 0x01, 0x00
+ };
+ static const uint8_t pkcs1_rsa_encryption[] = {
+ ASN1_ID_SEQUENCE, 0x0d,
+ ASN1_ID_OID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x01,
+ ASN1_ID_NULL, 0x00,
+ };
+
+ /*
+ * Sanity check that it's a version 0 or 1 RSAPrivateKey
+ * structure with the 8 integers, if it's not, make a last
+ * ditch attempt to load it into the kernel directly.
+ */
+ key_data = asn1_der_find_elem(content, len, 0, &tag,
+ &key_data_len);
+ if (!key_data || tag != ASN1_ID_SEQUENCE)
+ goto done;
+
+ data = asn1_der_find_elem(key_data, key_data_len, 0, &tag,
+ &data_len);
+ if (!data || tag != ASN1_ID_INTEGER || data_len != 1 ||
+ (data[0] != 0x00 && data[0] != 0x01))
+ goto done;
+
+ for (i = 1; i < 9; i++) {
+ data = asn1_der_find_elem(key_data, key_data_len,
+ i, &tag, &data_len);
+ if (!data || tag != ASN1_ID_INTEGER || data_len < 1)
+ goto done;
+ }
+
+ private_key = l_malloc(10 + len);
+ ptr = private_key;
+ *ptr++ = ASN1_ID_OCTET_STRING;
+ asn1_write_definite_length(&ptr, len);
+ memcpy(ptr, content, len);
+ ptr += len;
+ private_key_len = ptr - private_key;
+
+ one_asymmetric_key = l_malloc(32 + private_key_len);
+ ptr = one_asymmetric_key;
+ *ptr++ = ASN1_ID_SEQUENCE;
+ asn1_write_definite_length(&ptr,
+ sizeof(version0) +
+ sizeof(pkcs1_rsa_encryption) +
+ private_key_len);
+ memcpy(ptr, version0, sizeof(version0));
+ ptr += sizeof(version0);
+ memcpy(ptr, pkcs1_rsa_encryption, sizeof(pkcs1_rsa_encryption));
+ ptr += sizeof(pkcs1_rsa_encryption);
+ memcpy(ptr, private_key, private_key_len);
+ ptr += private_key_len;
+ l_free(private_key);
+
+ l_free(content);
+ content = one_asymmetric_key;
+ len = ptr - one_asymmetric_key;
+ goto done;
+ }
/* Label not known */
goto err;
--
2.25.1
1 year, 8 months
[PATCH 1/5] pem: Parse PKCS#1 formatted private keys
by Andrew Zaborowski
The PKCS#1 RSAPrivateKey format is used within the PKCS#8 PrivateKey
structure so we just need to wrap it in some extra ASN.1 data to get a
format understood by the pkcs8-key-parse module.
Improve some comments with the right RFC references.
---
ell/asn1-private.h | 1 +
ell/pem.c | 106 +++++++++++++++++++++++++++++++++++++++++----
2 files changed, 98 insertions(+), 9 deletions(-)
diff --git a/ell/asn1-private.h b/ell/asn1-private.h
index e34f977..2a31241 100644
--- a/ell/asn1-private.h
+++ b/ell/asn1-private.h
@@ -29,6 +29,7 @@
#define ASN1_ID_INTEGER ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x02)
#define ASN1_ID_BIT_STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x03)
#define ASN1_ID_OCTET_STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x04)
+#define ASN1_ID_NULL ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x05)
#define ASN1_ID_OID ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x06)
#define ASN1_ID_UTF8STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x0c)
#define ASN1_ID_PRINTABLESTRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x13)
diff --git a/ell/pem.c b/ell/pem.c
index b54270e..99020b1 100644
--- a/ell/pem.c
+++ b/ell/pem.c
@@ -400,14 +400,21 @@ static struct l_key *pem_load_private_key(uint8_t *content,
struct l_key *pkey = NULL;
/*
- * RFC7469- and PKCS#8-compatible label (default in OpenSSL 1.0.1+)
- * and the older (OpenSSL <= 0.9.8 default) label.
+ * RFC7468 Section 10-compatible unencrypted private key label
+ * (also mentioned in PKCS#8/RFC5958 Section 5), encodes
+ * the PKCS#8/RFC5958 PrivateKeyInfo structure -- supported
+ * directly by the pkcs8-key-parser kernel module.
*/
- if (!strcmp(label, "PRIVATE KEY") ||
- !strcmp(label, "RSA PRIVATE KEY"))
+ if (!strcmp(label, "PRIVATE KEY"))
goto done;
- /* RFC5958 (PKCS#8) section 3 type encrypted key label */
+ /*
+ * RFC7468 Section 11-compatible encrypted private key label
+ * (also mentioned in PKCS#8/RFC5958 Section 5), encodes
+ * the PKCS#8/RFC5958 EncryptedPrivateKeyInfo structure. We
+ * decrypt it into a plain PrivateKeyInfo for the
+ * pkcs8-key-parser module.
+ */
if (!strcmp(label, "ENCRYPTED PRIVATE KEY")) {
const uint8_t *key_info, *alg_id, *data;
uint8_t tag;
@@ -479,11 +486,92 @@ static struct l_key *pem_load_private_key(uint8_t *content,
}
/*
- * TODO: handle RSA PRIVATE KEY format encrypted keys
- * (as produced by "openssl rsa" commands), incompatible with
- * RFC7468 parsing because of the headers present before
- * base64-encoded data.
+ * Legacy RSA private key label aka. SSLeay format label, created
+ * by most software but not documented in an RFC. Encodes the
+ * PKCS#1/RFC8017 RSAPrivateKey structure. We wrap it in a PKCS#8
+ * PrivateKeyInfo for the pkcs8-key-parser module.
+ *
+ * TODO: decrypt RSA PRIVATE KEY format encrypted keys
+ * as produced by "openssl rsa" commands. These are incompatible
+ * with RFC7468 parsing because of the RFC822 headers present
+ * before base64-encoded data. The format is documented in
+ * RFC1421 and the encryption algorithms in RFC1423 although
+ * openssl allows many more algorithms than those documented.
+ * Then wrap in a PrivateKeyInfo like above.
*/
+ if (!strcmp(label, "RSA PRIVATE KEY")) {
+ const uint8_t *data;
+ uint8_t tag;
+ size_t data_len;
+ const uint8_t *key_data;
+ size_t key_data_len;
+ int i;
+ uint8_t *private_key;
+ size_t private_key_len;
+ uint8_t *one_asymmetric_key;
+ uint8_t *ptr;
+
+ static const uint8_t version0[] = {
+ ASN1_ID_INTEGER, 0x01, 0x00
+ };
+ static const uint8_t pkcs1_rsa_encryption[] = {
+ ASN1_ID_SEQUENCE, 0x0d,
+ ASN1_ID_OID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x01,
+ ASN1_ID_NULL, 0x00,
+ };
+
+ /*
+ * Sanity check that it's a version 0 or 1 RSAPrivateKey
+ * structure with the 8 integers, if it's not, make a last
+ * ditch attempt to load it into the kernel directly.
+ */
+ key_data = asn1_der_find_elem(content, len, 0, &tag,
+ &key_data_len);
+ if (!key_data || tag != ASN1_ID_SEQUENCE)
+ goto done;
+
+ data = asn1_der_find_elem(key_data, key_data_len, 0, &tag,
+ &data_len);
+ if (!data || tag != ASN1_ID_INTEGER || data_len != 1 ||
+ (data[0] != 0x00 && data[0] != 0x01))
+ goto done;
+
+ for (i = 1; i < 9; i++) {
+ data = asn1_der_find_elem(key_data, key_data_len,
+ i, &tag, &data_len);
+ if (!data || tag != ASN1_ID_INTEGER || data_len < 1)
+ goto done;
+ }
+
+ private_key = l_malloc(10 + len);
+ ptr = private_key;
+ *ptr++ = ASN1_ID_OCTET_STRING;
+ asn1_write_definite_length(&ptr, len);
+ memcpy(ptr, content, len);
+ ptr += len;
+ private_key_len = ptr - private_key;
+
+ one_asymmetric_key = l_malloc(32 + private_key_len);
+ ptr = one_asymmetric_key;
+ *ptr++ = ASN1_ID_SEQUENCE;
+ asn1_write_definite_length(&ptr,
+ sizeof(version0) +
+ sizeof(pkcs1_rsa_encryption) +
+ private_key_len);
+ memcpy(ptr, version0, sizeof(version0));
+ ptr += sizeof(version0);
+ memcpy(ptr, pkcs1_rsa_encryption, sizeof(pkcs1_rsa_encryption));
+ ptr += sizeof(pkcs1_rsa_encryption);
+ memcpy(ptr, private_key, private_key_len);
+ ptr += private_key_len;
+ l_free(private_key);
+
+ l_free(content);
+ content = one_asymmetric_key;
+ len = ptr - one_asymmetric_key;
+ goto done;
+ }
/* Label not known */
goto err;
--
2.25.1
1 year, 8 months