]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
Use a generic AF_ALG wrapper for common operations
authorMartin Willi <martin@revosec.ch>
Mon, 8 Nov 2010 09:59:54 +0000 (10:59 +0100)
committerMartin Willi <martin@revosec.ch>
Mon, 20 Dec 2010 08:52:02 +0000 (09:52 +0100)
src/libstrongswan/plugins/af_alg/Makefile.am
src/libstrongswan/plugins/af_alg/af_alg_ops.c [new file with mode: 0644]
src/libstrongswan/plugins/af_alg/af_alg_ops.h [new file with mode: 0644]

index 53e62bc292d38a2ca93cb7d66a58f314f1d340b1..f258d2422d6f116247253ace6847e1e67cc51cab 100644 (file)
@@ -11,6 +11,7 @@ endif
 
 libstrongswan_af_alg_la_SOURCES = \
        af_alg_plugin.h af_alg_plugin.c \
+       af_alg_ops.h af_alg_ops.c \
        af_alg_hasher.h af_alg_hasher.c \
        af_alg_signer.h af_alg_signer.c \
        af_alg_crypter.h af_alg_crypter.c
diff --git a/src/libstrongswan/plugins/af_alg/af_alg_ops.c b/src/libstrongswan/plugins/af_alg/af_alg_ops.c
new file mode 100644 (file)
index 0000000..37eeab5
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 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 "af_alg_ops.h"
+
+#include <unistd.h>
+#include <errno.h>
+#include <linux/socket.h>
+
+#include <debug.h>
+
+typedef struct private_af_alg_ops_t private_af_alg_ops_t;
+
+/**
+ * Private data of an af_alg_ops_t object.
+ */
+struct private_af_alg_ops_t {
+
+       /**
+        * Public af_alg_ops_t interface.
+        */
+       af_alg_ops_t public;
+
+       /**
+        * Transform FD
+        */
+       int tfm;
+
+       /**
+        * Operation FD
+        */
+       int op;
+};
+
+METHOD(af_alg_ops_t, reset, void,
+       private_af_alg_ops_t *this)
+{
+       if (this->op != -1)
+       {
+               close(this->op);
+               this->op = -1;
+       }
+}
+
+METHOD(af_alg_ops_t, hash, void,
+       private_af_alg_ops_t *this, chunk_t data, char *out, size_t outlen)
+{
+       ssize_t len;
+
+       while (this->op == -1)
+       {
+               this->op = accept(this->tfm, NULL, 0);
+               if (this->op == -1)
+               {
+                       DBG1(DBG_LIB, "opening AF_ALG hasher failed: %s", strerror(errno));
+                       sleep(1);
+               }
+       }
+       do
+       {
+               len = send(this->op, data.ptr, data.len, out ? 0 : MSG_MORE);
+               if (len == -1)
+               {
+                       DBG1(DBG_LIB, "writing to AF_ALG hasher failed: %s", strerror(errno));
+                       sleep(1);
+               }
+               else
+               {
+                       data = chunk_skip(data, len);
+               }
+       }
+       while (data.len);
+
+       if (out)
+       {
+               while (read(this->op, out, outlen) != outlen)
+               {
+                       DBG1(DBG_LIB, "reading AF_ALG hasher failed: %s", strerror(errno));
+                       sleep(1);
+               }
+               reset(this);
+       }
+}
+
+METHOD(af_alg_ops_t, crypt, void,
+       private_af_alg_ops_t *this, u_int32_t type, chunk_t iv, chunk_t data,
+       char *out)
+{
+       struct msghdr msg = {};
+       struct cmsghdr *cmsg;
+       struct af_alg_iv *ivm;
+       struct iovec iov;
+       char buf[CMSG_SPACE(sizeof(type)) +
+                        CMSG_SPACE(offsetof(struct af_alg_iv, iv) + iv.len)];
+       ssize_t len;
+       int op;
+
+       while ((op = accept(this->tfm, NULL, 0)) == -1)
+       {
+               DBG1(DBG_LIB, "accepting AF_ALG crypter failed: %s", strerror(errno));
+               sleep(1);
+       }
+
+       memset(buf, 0, sizeof(buf));
+
+       msg.msg_control = buf;
+       msg.msg_controllen = sizeof(buf);
+
+       cmsg = CMSG_FIRSTHDR(&msg);
+       cmsg->cmsg_level = SOL_ALG;
+       cmsg->cmsg_type = ALG_SET_OP;
+       cmsg->cmsg_len = CMSG_LEN(sizeof(type));
+       *(u_int32_t*)CMSG_DATA(cmsg) = type;
+
+       cmsg = CMSG_NXTHDR(&msg, cmsg);
+       cmsg->cmsg_level = SOL_ALG;
+       cmsg->cmsg_type = ALG_SET_IV;
+       cmsg->cmsg_len = CMSG_LEN(offsetof(struct af_alg_iv, iv) + iv.len);
+       ivm = (void*)CMSG_DATA(cmsg);
+       ivm->ivlen = iv.len;
+       memcpy(ivm->iv, iv.ptr, iv.len);
+
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       while (data.len)
+       {
+               iov.iov_base = data.ptr;
+               iov.iov_len = data.len;
+
+               len = sendmsg(op, &msg, 0);
+               if (len == -1)
+               {
+                       DBG1(DBG_LIB, "writing to AF_ALG crypter failed: %s",
+                                strerror(errno));
+                       sleep(1);
+                       continue;
+               }
+               if (read(op, out, len) != len)
+               {
+                       DBG1(DBG_LIB, "reading from AF_ALG crypter failed: %s",
+                                strerror(errno));
+               }
+               data = chunk_skip(data, len);
+               /* no IV for subsequent data chunks */
+               msg.msg_controllen = 0;
+       }
+       close(op);
+}
+
+METHOD(af_alg_ops_t, set_key, void,
+       private_af_alg_ops_t *this, chunk_t key)
+{
+       if (setsockopt(this->tfm, SOL_ALG, ALG_SET_KEY, key.ptr, key.len) == -1)
+       {
+               DBG1(DBG_LIB, "setting AF_ALG key failed: %s", strerror(errno));
+       }
+}
+
+METHOD(af_alg_ops_t, destroy, void,
+       private_af_alg_ops_t *this)
+{
+       close(this->tfm);
+       if (this->op != -1)
+       {
+               close(this->op);
+       }
+       free(this);
+}
+
+/**
+ * See header
+ */
+af_alg_ops_t *af_alg_ops_create(char *type, char *alg)
+{
+       private_af_alg_ops_t *this;
+       struct sockaddr_alg sa = {
+               .salg_family = AF_ALG,
+       };
+
+       strncpy(sa.salg_type, type, sizeof(sa.salg_type));
+       strncpy(sa.salg_name, alg, sizeof(sa.salg_name));
+
+       INIT(this,
+               .public = {
+                       .hash = _hash,
+                       .reset = _reset,
+                       .crypt = _crypt,
+                       .set_key = _set_key,
+                       .destroy = _destroy,
+               },
+               .tfm = socket(AF_ALG, SOCK_SEQPACKET, 0),
+               .op = -1,
+       );
+       if (this->tfm == -1)
+       {
+               DBG1(DBG_LIB, "opening AF_ALG socket failed: %s", strerror(errno));
+               free(this);
+               return NULL;
+       }
+       if (bind(this->tfm, (struct sockaddr*)&sa, sizeof(sa)) == -1)
+       {
+               DBG1(DBG_LIB, "binding AF_ALG socket for '%s' failed: %s",
+                        sa.salg_name, strerror(errno));
+               destroy(this);
+               return NULL;
+       }
+       return &this->public;
+}
diff --git a/src/libstrongswan/plugins/af_alg/af_alg_ops.h b/src/libstrongswan/plugins/af_alg/af_alg_ops.h
new file mode 100644 (file)
index 0000000..ad16402
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 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 af_alg_ops af_alg_ops
+ * @{ @ingroup af_alg
+ */
+
+#ifndef AF_ALG_OPS_H_
+#define AF_ALG_OPS_H_
+
+#include <library.h>
+
+#include <linux/if_alg.h>
+
+#ifndef AF_ALG
+#define AF_ALG 38
+#endif /* AF_ALG */
+
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif /* SOL_ALG */
+
+typedef struct af_alg_ops_t af_alg_ops_t;
+
+/**
+ * Helper to run AF_ALG operations.
+ */
+struct af_alg_ops_t {
+
+       /**
+        * Hash a chunk of data.
+        *
+        * @param data          data to hash
+        * @param out           buffer to write hash to, NULL for append mode
+        * @param outlen        number of bytes to read into out
+        */
+       void (*hash)(af_alg_ops_t *this, chunk_t data, char *out, size_t outlen);
+
+       /**
+        * Reset hasher state.
+        */
+       void (*reset)(af_alg_ops_t *this);
+
+       /**
+        * En-/Decrypt a chunk of data.
+        *
+        * @param type          crypto operation (ALG_OP_DECRYPT/ALG_OP_ENCRYPT)
+        * @param iv            iv to use
+        * @param data          data to encrypt/decrypt
+        * @param out           buffer write processed data to
+        */
+       void (*crypt)(af_alg_ops_t *this, u_int32_t type, chunk_t iv, chunk_t data,
+                                 char *out);
+
+       /**
+        * Set the key for en-/decryption or HMAC/XCBC operations.
+        *
+        * @param key           key to set for transform
+        */
+       void (*set_key)(af_alg_ops_t *this, chunk_t key);
+
+       /**
+        * Destroy a af_alg_ops_t.
+        */
+       void (*destroy)(af_alg_ops_t *this);
+};
+
+/**
+ * Create a af_alg_ops instance.
+ *
+ * @param type                 algorithm type (hash, skcipher)
+ * @param alg                  algorithm name
+ * @return                             TRUE if AF_ALG socket bound successfully
+ */
+af_alg_ops_t *af_alg_ops_create(char *type, char *alg);
+
+#endif /** AF_ALG_OPS_H_ @}*/