Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
yubihsm_curl.c
Go to the documentation of this file.
1/*
2 * Copyright 2015-2018 Yubico AB
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <string.h>
18#include <errno.h>
19
20#include <arpa/inet.h>
21
22#include "yubihsm.h"
23#include "internal.h"
24#include "debug_lib.h"
25
26#include "curl/curl.h"
27
28struct state {
29 CURL *curl;
30};
31
37
40
41static size_t curl_callback_write(void *ptr, size_t size, size_t nmemb,
42 void *stream) {
43
44 struct curl_data *data = (struct curl_data *) stream;
45
46 if (data->size + size * nmemb < data->size ||
47 data->size + size * nmemb > data->max_size) {
48 return 0;
49 }
50
51 memcpy(data->data + data->size, ptr, size * nmemb);
52 data->size += size * nmemb;
53
54 return size * nmemb;
55}
56
57static void backend_set_verbosity(uint8_t verbosity, FILE *output) {
58 _yh_verbosity = verbosity;
59 _yh_output = output;
60}
61
62static yh_rc backend_init(uint8_t verbosity, FILE *output) {
63 CURLcode rc;
64
65 backend_set_verbosity(verbosity, output);
66
67 rc = curl_global_init(
68 CURL_GLOBAL_DEFAULT); // NOTE(adma): this funciton is not thread safe
69 if (rc != CURLE_OK) {
70 DBG_ERR("%s", curl_easy_strerror(rc));
72 }
73
74 return YHR_SUCCESS;
75}
76
77static yh_backend *backend_create() { return curl_easy_init(); }
78
79static yh_rc backend_connect(yh_connector *connector, int timeout) {
80
81 CURLcode rc;
82 struct url_data {
83 char scratch[257];
84 struct curl_data curl_data;
85 } data;
86 char curl_error[CURL_ERROR_SIZE] = {0};
87
88 DBG_INFO("Trying to connect to %s", connector->status_url);
89
90 curl_easy_setopt(connector->connection, CURLOPT_URL, connector->status_url);
91 curl_easy_setopt(connector->connection, CURLOPT_CONNECTTIMEOUT, timeout);
92#ifdef CURLOPT_TCP_KEEPALIVE
93 curl_easy_setopt(connector->connection, CURLOPT_TCP_KEEPALIVE, 1);
94#endif /* CURLOPT_TCP_KEEPALIVE */
95 curl_easy_setopt(connector->connection, CURLOPT_FAILONERROR, 1);
96 curl_easy_setopt(connector->connection, CURLOPT_USERAGENT,
97 "YubiHSM curl/" VERSION);
98
99 curl_easy_setopt(connector->connection, CURLOPT_WRITEFUNCTION,
100 curl_callback_write);
101
102 curl_easy_setopt(connector->connection, CURLOPT_ERRORBUFFER, curl_error);
103
104 memset((uint8_t *) data.scratch, 0, sizeof(data.scratch));
105 data.curl_data.data = (uint8_t *) data.scratch;
106 data.curl_data.size = 0;
107 data.curl_data.max_size = sizeof(data.scratch) - 1;
108 curl_easy_setopt(connector->connection, CURLOPT_WRITEDATA, &data.curl_data);
109
110 rc = curl_easy_perform(connector->connection);
111 if (rc != CURLE_OK) {
112 if (strlen(curl_error) > 0) {
113 DBG_ERR("Failure when connecting: '%s'", curl_error);
114 } else {
115 DBG_ERR("Failure when connecting: '%s'", curl_easy_strerror(rc));
116 }
118 }
119
120 if (strlen(data.scratch) != data.curl_data.size) {
121 DBG_ERR("Amount of data received does not match scratch buffer. Expected "
122 "%zu, found %d",
123 strlen(data.scratch), data.curl_data.size);
124 return YHR_GENERIC_ERROR;
125 }
126
127 parse_status_data(data.scratch, connector);
128
129 DBG_INFO("Found working connector");
130
131 curl_easy_setopt(connector->connection, CURLOPT_URL, connector->api_url);
132
133 return YHR_SUCCESS;
134}
135
136static void backend_disconnect(yh_backend *connection) {
137 curl_easy_cleanup(connection);
138}
139
140static yh_rc backend_send_msg(yh_backend *connection, Msg *msg, Msg *response) {
141 CURLcode rc;
143 int32_t trf_len = msg->st.len + 3;
144 struct curl_data data = {response->raw, 0, SCP_MSG_BUF_SIZE};
145 struct curl_slist *headers = NULL;
146 char curl_error[CURL_ERROR_SIZE] = {0};
147
148 headers =
149 curl_slist_append(headers, "Content-Type: application/octet-stream");
150
151 curl_easy_setopt(connection, CURLOPT_HTTPHEADER, headers);
152 curl_easy_setopt(connection, CURLOPT_POSTFIELDS, (void *) msg->raw);
153 curl_easy_setopt(connection, CURLOPT_POSTFIELDSIZE, trf_len);
154
155 curl_easy_setopt(connection, CURLOPT_WRITEDATA, &data);
156 curl_easy_setopt(connection, CURLOPT_ERRORBUFFER, curl_error);
157
158 // Endian swap length
159 msg->st.len = htons(msg->st.len);
160
161 // NOTE(adma): connection is actually established here the first time
162 rc = curl_easy_perform(connection);
163 curl_slist_free_all(headers);
164 if (rc != CURLE_OK) {
165 goto sm_failure;
166 }
167
168 if (data.size < 3) {
169 DBG_ERR("Not enough data received: %d", data.size);
170 return YHR_WRONG_LENGTH;
171 }
172
173 response->st.len = ntohs(response->st.len);
174
175 if (response->st.len != data.size - 3) {
176 DBG_ERR("Wrong length received, %d vs %d", response->st.len, data.size);
177 return YHR_WRONG_LENGTH;
178 }
179
180 return YHR_SUCCESS;
181
182sm_failure:
183
184 if (strlen(curl_error) > 0) {
185 DBG_ERR("Curl perform failed: '%s'", curl_error);
186 } else {
187 DBG_ERR("Curl perform failed: '%s'", curl_easy_strerror(rc));
188 }
189
190 // Restore original value
191 msg->st.len = ntohs(msg->st.len);
192
193 // Clear response length
194 response->st.len = 0;
195
196 return yrc;
197}
198
199static void backend_cleanup(void) {
200 /* by all rights we should call curl_global_cleanup() here, but.. if curl is
201 * using openssl that will cleanup all openssl context, which if we're called
202 * through pkcs11_engine and our pkcs11 module will break everything, so we
203 * don't. */
204 // curl_global_cleanup();
205}
206
207static yh_rc backend_option(yh_backend *connection, yh_connector_option opt,
208 const void *val) {
209 CURLoption option;
210 const char *optname;
211
212 switch (opt) {
214 option = CURLOPT_CAINFO;
215 optname = "CURLOPT_CAINFO";
216 break;
218 option = CURLOPT_PROXY;
219 optname = "CURLOPT_PROXY";
220 break;
221 default:
222 DBG_ERR("%d is an unknown option", opt);
224 }
225 CURLcode rc = curl_easy_setopt(connection, option, (char *) val);
226 if (rc == CURLE_OK) {
227 DBG_INFO("Successfully set %s.", optname);
228 return YHR_SUCCESS;
229 } else {
230 DBG_ERR("Failed to set %s (%d): %s", optname, rc, curl_easy_strerror(rc));
231 return YHR_CONNECTOR_ERROR;
232 }
233}
234
239
240#ifdef STATIC
241struct backend_functions *http_backend_functions(void) {
242#else
244#endif
245 return &f;
246}
#define YH_INTERNAL
Definition aes.h:58
#define DBG_ERR(...)
Definition debug_lib.h:76
#define DBG_INFO(...)
Definition debug_lib.h:63
void YH_INTERNAL parse_status_data(char *data, yh_connector *connector)
Definition lib_util.c:65
vector< string > headers
Definition main.cpp:170
#define SCP_MSG_BUF_SIZE
Definition scp.h:52
unsigned short uint16_t
Definition stdint.h:125
signed int int32_t
Definition stdint.h:123
unsigned char uint8_t
Definition stdint.h:124
yh_rc(* backend_init)(uint8_t verbosity, FILE *output)
Definition internal.h:66
void(* backend_set_verbosity)(uint8_t verbosity, FILE *output)
Definition internal.h:74
void(* backend_disconnect)(yh_backend *connection)
Definition internal.h:69
yh_rc(* backend_send_msg)(yh_backend *connection, Msg *msg, Msg *response)
Definition internal.h:70
yh_rc(* backend_option)(yh_backend *connection, yh_connector_option opt, const void *val)
Definition internal.h:72
yh_backend *(* backend_create)(void)
Definition internal.h:67
void(* backend_cleanup)(void)
Definition internal.h:71
yh_rc(* backend_connect)(yh_connector *connector, int timeout)
Definition internal.h:68
uint8_t * data
uint16_t max_size
uint16_t size
CURL * curl
char * status_url
Definition internal.h:41
char * api_url
Definition internal.h:42
yh_backend * connection
Definition internal.h:40
Definition scp.h:56
struct _Msg::@103 st
uint8_t raw[3+SCP_MSG_BUF_SIZE]
Definition scp.h:62
yh_option option
Definition yubihsm.h:685
yh_connector_option
Definition yubihsm.h:500
@ YH_CONNECTOR_PROXY_SERVER
Definition yubihsm.h:506
@ YH_CONNECTOR_HTTPS_CA
Definition yubihsm.h:503
yh_rc
Definition yubihsm.h:170
@ YHR_GENERIC_ERROR
Return value when encountering an unknown error.
Definition yubihsm.h:228
@ YHR_SUCCESS
Returned value when function was successful.
Definition yubihsm.h:172
@ YHR_INVALID_PARAMETERS
Returned value when an argument to a function is invalid.
Definition yubihsm.h:182
@ YHR_CONNECTOR_NOT_FOUND
Returned value when failing to find a suitable connector.
Definition yubihsm.h:180
@ YHR_CONNECTOR_ERROR
Return value when connector operation failed.
Definition yubihsm.h:232
@ YHR_WRONG_LENGTH
Definition yubihsm.h:185
@ YHR_CONNECTION_ERROR
Returned value when a connection error was encountered.
Definition yubihsm.h:178
FILE YH_INTERNAL * _yh_output
struct backend_functions * backend_functions(void)
uint8_t YH_INTERNAL _yh_verbosity
yh_rc rc
yh_rc yrc
memset(pInfo->slotDescription, ' ', 64)
memcpy((char *) pInfo->slotDescription, s, l)