82a227d978a86e53927d03bc406489f855e68baf
[strongswan.git] / src / libstrongswan / plugins / af_alg / af_alg_ops.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "af_alg_ops.h"
17
18 #include <unistd.h>
19 #include <errno.h>
20 #include <linux/socket.h>
21
22 #include <debug.h>
23
24 typedef struct private_af_alg_ops_t private_af_alg_ops_t;
25
26 /**
27 * Private data of an af_alg_ops_t object.
28 */
29 struct private_af_alg_ops_t {
30
31 /**
32 * Public af_alg_ops_t interface.
33 */
34 af_alg_ops_t public;
35
36 /**
37 * Transform FD
38 */
39 int tfm;
40
41 /**
42 * Operation FD
43 */
44 int op;
45 };
46
47 METHOD(af_alg_ops_t, reset, void,
48 private_af_alg_ops_t *this)
49 {
50 if (this->op != -1)
51 {
52 close(this->op);
53 this->op = -1;
54 }
55 }
56
57 METHOD(af_alg_ops_t, hash, void,
58 private_af_alg_ops_t *this, chunk_t data, char *out, size_t outlen)
59 {
60 ssize_t len;
61
62 while (this->op == -1)
63 {
64 this->op = accept(this->tfm, NULL, 0);
65 if (this->op == -1)
66 {
67 DBG1(DBG_LIB, "opening AF_ALG hasher failed: %s", strerror(errno));
68 sleep(1);
69 }
70 }
71 do
72 {
73 len = send(this->op, data.ptr, data.len, out ? 0 : MSG_MORE);
74 if (len == -1)
75 {
76 DBG1(DBG_LIB, "writing to AF_ALG hasher failed: %s", strerror(errno));
77 sleep(1);
78 }
79 else
80 {
81 data = chunk_skip(data, len);
82 }
83 }
84 while (data.len);
85
86 if (out)
87 {
88 while (read(this->op, out, outlen) != outlen)
89 {
90 DBG1(DBG_LIB, "reading AF_ALG hasher failed: %s", strerror(errno));
91 sleep(1);
92 }
93 reset(this);
94 }
95 }
96
97 METHOD(af_alg_ops_t, crypt, void,
98 private_af_alg_ops_t *this, u_int32_t type, chunk_t iv, chunk_t data,
99 char *out)
100 {
101 struct msghdr msg = {};
102 struct cmsghdr *cmsg;
103 struct af_alg_iv *ivm;
104 struct iovec iov;
105 char buf[CMSG_SPACE(sizeof(type)) +
106 CMSG_SPACE(offsetof(struct af_alg_iv, iv) + iv.len)];
107 ssize_t len;
108 int op;
109
110 while ((op = accept(this->tfm, NULL, 0)) == -1)
111 {
112 DBG1(DBG_LIB, "accepting AF_ALG crypter failed: %s", strerror(errno));
113 sleep(1);
114 }
115
116 memset(buf, 0, sizeof(buf));
117
118 msg.msg_control = buf;
119 msg.msg_controllen = sizeof(buf);
120
121 cmsg = CMSG_FIRSTHDR(&msg);
122 cmsg->cmsg_level = SOL_ALG;
123 cmsg->cmsg_type = ALG_SET_OP;
124 cmsg->cmsg_len = CMSG_LEN(sizeof(type));
125 *(u_int32_t*)CMSG_DATA(cmsg) = type;
126
127 cmsg = CMSG_NXTHDR(&msg, cmsg);
128 cmsg->cmsg_level = SOL_ALG;
129 cmsg->cmsg_type = ALG_SET_IV;
130 cmsg->cmsg_len = CMSG_LEN(offsetof(struct af_alg_iv, iv) + iv.len);
131 ivm = (void*)CMSG_DATA(cmsg);
132 ivm->ivlen = iv.len;
133 memcpy(ivm->iv, iv.ptr, iv.len);
134
135 msg.msg_iov = &iov;
136 msg.msg_iovlen = 1;
137
138 while (data.len)
139 {
140 iov.iov_base = data.ptr;
141 iov.iov_len = data.len;
142
143 len = sendmsg(op, &msg, 0);
144 if (len == -1)
145 {
146 DBG1(DBG_LIB, "writing to AF_ALG crypter failed: %s",
147 strerror(errno));
148 sleep(1);
149 continue;
150 }
151 if (read(op, out, len) != len)
152 {
153 DBG1(DBG_LIB, "reading from AF_ALG crypter failed: %s",
154 strerror(errno));
155 }
156 data = chunk_skip(data, len);
157 /* no IV for subsequent data chunks */
158 msg.msg_controllen = 0;
159 }
160 close(op);
161 }
162
163 METHOD(af_alg_ops_t, set_key, void,
164 private_af_alg_ops_t *this, chunk_t key)
165 {
166 if (setsockopt(this->tfm, SOL_ALG, ALG_SET_KEY, key.ptr, key.len) == -1)
167 {
168 DBG1(DBG_LIB, "setting AF_ALG key failed: %s", strerror(errno));
169 }
170 }
171
172 METHOD(af_alg_ops_t, destroy, void,
173 private_af_alg_ops_t *this)
174 {
175 close(this->tfm);
176 if (this->op != -1)
177 {
178 close(this->op);
179 }
180 free(this);
181 }
182
183 /**
184 * See header
185 */
186 af_alg_ops_t *af_alg_ops_create(char *type, char *alg)
187 {
188 private_af_alg_ops_t *this;
189 struct sockaddr_alg sa = {
190 .salg_family = AF_ALG,
191 };
192
193 strncpy(sa.salg_type, type, sizeof(sa.salg_type));
194 strncpy(sa.salg_name, alg, sizeof(sa.salg_name));
195
196 INIT(this,
197 .public = {
198 .hash = _hash,
199 .reset = _reset,
200 .crypt = _crypt,
201 .set_key = _set_key,
202 .destroy = _destroy,
203 },
204 .tfm = socket(AF_ALG, SOCK_SEQPACKET, 0),
205 .op = -1,
206 );
207 if (this->tfm == -1)
208 {
209 DBG1(DBG_LIB, "opening AF_ALG socket failed: %s", strerror(errno));
210 free(this);
211 return NULL;
212 }
213 if (bind(this->tfm, (struct sockaddr*)&sa, sizeof(sa)) == -1)
214 {
215 if (errno != ENOENT)
216 { /* fail silently if algorithm not supported */
217 DBG1(DBG_LIB, "binding AF_ALG socket for '%s' failed: %s",
218 sa.salg_name, strerror(errno));
219 }
220 destroy(this);
221 return NULL;
222 }
223 return &this->public;
224 }