Check and forward syscall errors in AF_ALG
[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, bool,
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 && errno != EINTR)
66 {
67 DBG1(DBG_LIB, "opening AF_ALG hasher failed: %s", strerror(errno));
68 return FALSE;
69 }
70 }
71
72 do
73 {
74 len = send(this->op, data.ptr, data.len, out ? 0 : MSG_MORE);
75 if (len == -1)
76 {
77 if (errno == EINTR)
78 {
79 continue;
80 }
81 DBG1(DBG_LIB, "writing to AF_ALG hasher failed: %s", strerror(errno));
82 return FALSE;
83 }
84 data = chunk_skip(data, len);
85 }
86 while (data.len);
87
88 if (out)
89 {
90 while (outlen)
91 {
92 len = read(this->op, out, outlen);
93 if (len == -1)
94 {
95 if (errno == EINTR)
96 {
97 continue;
98 }
99 DBG1(DBG_LIB, "reading AF_ALG hasher failed: %s", strerror(errno));
100 return FALSE;
101 }
102 outlen -= len;
103 out += len;
104 }
105 reset(this);
106 }
107 return TRUE;
108 }
109
110 METHOD(af_alg_ops_t, crypt, bool,
111 private_af_alg_ops_t *this, u_int32_t type, chunk_t iv, chunk_t data,
112 char *out)
113 {
114 struct msghdr msg = {};
115 struct cmsghdr *cmsg;
116 struct af_alg_iv *ivm;
117 struct iovec iov;
118 char buf[CMSG_SPACE(sizeof(type)) +
119 CMSG_SPACE(offsetof(struct af_alg_iv, iv) + iv.len)];
120 ssize_t len;
121 int op;
122
123 do
124 {
125 op = accept(this->tfm, NULL, 0);
126 if (op == -1 && errno != EINTR)
127 {
128 DBG1(DBG_LIB, "accepting AF_ALG crypter failed: %s", strerror(errno));
129 return FALSE;
130 }
131 }
132 while (op == -1);
133
134 memset(buf, 0, sizeof(buf));
135
136 msg.msg_control = buf;
137 msg.msg_controllen = sizeof(buf);
138
139 cmsg = CMSG_FIRSTHDR(&msg);
140 cmsg->cmsg_level = SOL_ALG;
141 cmsg->cmsg_type = ALG_SET_OP;
142 cmsg->cmsg_len = CMSG_LEN(sizeof(type));
143 memcpy(CMSG_DATA(cmsg), &type, sizeof(type));
144
145 cmsg = CMSG_NXTHDR(&msg, cmsg);
146 cmsg->cmsg_level = SOL_ALG;
147 cmsg->cmsg_type = ALG_SET_IV;
148 cmsg->cmsg_len = CMSG_LEN(offsetof(struct af_alg_iv, iv) + iv.len);
149 ivm = (void*)CMSG_DATA(cmsg);
150 ivm->ivlen = iv.len;
151 memcpy(ivm->iv, iv.ptr, iv.len);
152
153 msg.msg_iov = &iov;
154 msg.msg_iovlen = 1;
155
156 while (data.len)
157 {
158 iov.iov_base = data.ptr;
159 iov.iov_len = data.len;
160
161 len = sendmsg(op, &msg, 0);
162 if (len == -1)
163 {
164 if (errno == EINTR)
165 {
166 continue;
167 }
168 DBG1(DBG_LIB, "writing to AF_ALG crypter failed: %s", strerror(errno));
169 return FALSE;
170 }
171 while (read(op, out, len) != len)
172 {
173 if (errno != EINTR)
174 {
175 DBG1(DBG_LIB, "reading from AF_ALG crypter failed: %s",
176 strerror(errno));
177 return FALSE;
178 }
179 }
180 data = chunk_skip(data, len);
181 /* no IV for subsequent data chunks */
182 msg.msg_controllen = 0;
183 }
184 close(op);
185 return TRUE;
186 }
187
188 METHOD(af_alg_ops_t, set_key, bool,
189 private_af_alg_ops_t *this, chunk_t key)
190 {
191 if (setsockopt(this->tfm, SOL_ALG, ALG_SET_KEY, key.ptr, key.len) == -1)
192 {
193 DBG1(DBG_LIB, "setting AF_ALG key failed: %s", strerror(errno));
194 return FALSE;
195 }
196 return TRUE;
197 }
198
199 METHOD(af_alg_ops_t, destroy, void,
200 private_af_alg_ops_t *this)
201 {
202 close(this->tfm);
203 if (this->op != -1)
204 {
205 close(this->op);
206 }
207 free(this);
208 }
209
210 /**
211 * See header
212 */
213 af_alg_ops_t *af_alg_ops_create(char *type, char *alg)
214 {
215 private_af_alg_ops_t *this;
216 struct sockaddr_alg sa = {
217 .salg_family = AF_ALG,
218 };
219
220 strncpy(sa.salg_type, type, sizeof(sa.salg_type));
221 strncpy(sa.salg_name, alg, sizeof(sa.salg_name));
222
223 INIT(this,
224 .public = {
225 .hash = _hash,
226 .reset = _reset,
227 .crypt = _crypt,
228 .set_key = _set_key,
229 .destroy = _destroy,
230 },
231 .tfm = socket(AF_ALG, SOCK_SEQPACKET, 0),
232 .op = -1,
233 );
234 if (this->tfm == -1)
235 {
236 DBG1(DBG_LIB, "opening AF_ALG socket failed: %s", strerror(errno));
237 free(this);
238 return NULL;
239 }
240 if (bind(this->tfm, (struct sockaddr*)&sa, sizeof(sa)) == -1)
241 {
242 if (errno != ENOENT)
243 { /* fail silently if algorithm not supported */
244 DBG1(DBG_LIB, "binding AF_ALG socket for '%s' failed: %s",
245 sa.salg_name, strerror(errno));
246 }
247 destroy(this);
248 return NULL;
249 }
250 return &this->public;
251 }