1 #include <petscwebclient.h>
2 PETSC_PRAGMA_DIAGNOSTIC_IGNORED_BEGIN("-Wdeprecated-declarations")
3
4 static BIO *bio_err = NULL;
5
6 #define PASSWORD "password"
7
8 #if defined(PETSC_USE_SSL_CERTIFICATE)
password_cb(char * buf,int num,int rwflag,void * userdata)9 static int password_cb(char *buf, int num, int rwflag, void *userdata)
10 {
11 if (num < strlen(PASSWORD) + 1) return 0;
12 strcpy(buf, PASSWORD);
13 return strlen(PASSWORD);
14 }
15 #endif
16
sigpipe_handle(int x)17 static void sigpipe_handle(int x) { }
18
19 /*@C
20 PetscSSLInitializeContext - Set up an SSL context suitable for initiating HTTPS requests.
21
22 Output Parameter:
23 . octx - the SSL_CTX to be passed to `PetscHTTPSConnect90`
24
25 Level: advanced
26
27 If PETSc was ./configure -with-ssl-certificate requires the user have created a self-signed certificate with
28 .vb
29 saws/CA.pl -newcert (using the passphrase of password)
30 cat newkey.pem newcert.pem > sslclient.pem
31 .ve
32
33 and put the resulting file in either the current directory (with the application) or in the home directory. This seems kind of
34 silly but it was all I could figure out.
35
36 .seealso: `PetscSSLDestroyContext()`, `PetscHTTPSConnect()`, `PetscHTTPSRequest()`
37 @*/
PetscSSLInitializeContext(SSL_CTX ** octx)38 PetscErrorCode PetscSSLInitializeContext(SSL_CTX **octx)
39 {
40 SSL_CTX *ctx;
41 #if defined(PETSC_USE_SSL_CERTIFICATE)
42 char keyfile[PETSC_MAX_PATH_LEN];
43 PetscBool exists;
44 #endif
45
46 PetscFunctionBegin;
47 if (!bio_err) {
48 SSL_library_init();
49 SSL_load_error_strings();
50 bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
51 }
52
53 /* Set up a SIGPIPE handler */
54 signal(SIGPIPE, sigpipe_handle);
55
56 /* suggested at https://mta.openssl.org/pipermail/openssl-dev/2015-May/001449.html */
57 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
58 ctx = SSL_CTX_new(TLS_client_method());
59 #else
60 ctx = SSL_CTX_new(SSLv23_client_method());
61 #endif
62 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
63
64 #if defined(PETSC_USE_SSL_CERTIFICATE)
65 /* Locate keyfile */
66 PetscCall(PetscStrncpy(keyfile, "sslclient.pem", sizeof(keyfile)));
67 PetscCall(PetscTestFile(keyfile, 'r', &exists));
68 if (!exists) {
69 PetscCall(PetscGetHomeDirectory(keyfile, PETSC_MAX_PATH_LEN));
70 PetscCall(PetscStrlcat(keyfile, "/", sizeof(keyfile)));
71 PetscCall(PetscStrlcat(keyfile, "sslclient.pem", sizeof(keyfile)));
72 PetscCall(PetscTestFile(keyfile, 'r', &exists));
73 PetscCheck(exists, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate sslclient.pem file in current directory or home directory");
74 }
75
76 /* Load our keys and certificates*/
77 PetscCheck(SSL_CTX_use_certificate_chain_file(ctx, keyfile), PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot read certificate file");
78
79 SSL_CTX_set_default_passwd_cb(ctx, password_cb);
80 PetscCheck(SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM), PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot read key file");
81 #endif
82
83 *octx = ctx;
84 PetscFunctionReturn(PETSC_SUCCESS);
85 }
86
87 /*@C
88 PetscSSLDestroyContext - frees a `SSL_CTX` obtained with `PetscSSLInitializeContext()`
89
90 Input Parameter:
91 . ctx - the `SSL_CTX`
92
93 Level: advanced
94
95 .seealso: `PetscSSLInitializeContext()`, `PetscHTTPSConnect()`
96 @*/
PetscSSLDestroyContext(SSL_CTX * ctx)97 PetscErrorCode PetscSSLDestroyContext(SSL_CTX *ctx)
98 {
99 PetscFunctionBegin;
100 SSL_CTX_free(ctx);
101 PetscFunctionReturn(PETSC_SUCCESS);
102 }
103
PetscHTTPBuildRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],char ** outrequest)104 static PetscErrorCode PetscHTTPBuildRequest(const char type[], const char url[], const char header[], const char ctype[], const char body[], char **outrequest)
105 {
106 char *request = 0;
107 char contentlength[40], contenttype[80], *path, *host;
108 size_t request_len, headlen, bodylen, contentlen, pathlen, hostlen, typelen, contenttypelen = 0;
109 PetscBool flg;
110
111 PetscFunctionBegin;
112 PetscCall(PetscStrallocpy(url, &host));
113 PetscCall(PetscStrchr(host, '/', &path));
114 PetscCheck(path, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "URL must contain /, it is %s", url);
115 *path = 0;
116 PetscCall(PetscStrlen(host, &hostlen));
117
118 PetscCall(PetscStrchr(url, '/', &path));
119 PetscCall(PetscStrlen(path, &pathlen));
120
121 if (header) {
122 PetscCall(PetscStrendswith(header, "\r\n", &flg));
123 PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Header must end with \\r\\n, it is %s", header);
124 }
125
126 PetscCall(PetscStrlen(type, &typelen));
127 if (ctype) {
128 PetscCall(PetscSNPrintf(contenttype, 80, "Content-Type: %s\r\n", ctype));
129 PetscCall(PetscStrlen(contenttype, &contenttypelen));
130 }
131 PetscCall(PetscStrlen(header, &headlen));
132 PetscCall(PetscStrlen(body, &bodylen));
133 PetscCall(PetscSNPrintf(contentlength, 40, "Content-Length: %d\r\n\r\n", (int)bodylen));
134 PetscCall(PetscStrlen(contentlength, &contentlen));
135
136 /* Now construct our HTTP request */
137 request_len = typelen + 1 + pathlen + hostlen + 100 + headlen + contenttypelen + contentlen + bodylen + 1;
138 PetscCall(PetscMalloc1(request_len, &request));
139 PetscCall(PetscStrncpy(request, type, request_len));
140 PetscCall(PetscStrlcat(request, " ", request_len));
141 PetscCall(PetscStrlcat(request, path, request_len));
142 PetscCall(PetscStrlcat(request, " HTTP/1.1\r\nHost: ", request_len));
143 PetscCall(PetscStrlcat(request, host, request_len));
144 PetscCall(PetscFree(host));
145 PetscCall(PetscStrlcat(request, "\r\nUser-Agent:PETScClient\r\n", request_len));
146 PetscCall(PetscStrlcat(request, header, request_len));
147 if (ctype) PetscCall(PetscStrlcat(request, contenttype, request_len));
148 PetscCall(PetscStrlcat(request, contentlength, request_len));
149 PetscCall(PetscStrlcat(request, body, request_len));
150 PetscCall(PetscStrlen(request, &request_len));
151 PetscCall(PetscInfo(NULL, "HTTPS request follows: \n%s\n", request));
152
153 *outrequest = request;
154 PetscFunctionReturn(PETSC_SUCCESS);
155 }
156
157 /*@C
158 PetscHTTPSRequest - Send a request to an HTTPS server
159
160 Input Parameters:
161 + type - either "POST" or "GET"
162 . url - URL of request host/path
163 . header - additional header information, may be `NULL`
164 . ctype - data type of body, for example application/json
165 . body - data to send to server
166 . ssl - obtained with `PetscHTTPSConnect()`
167 - buffsize - size of buffer
168
169 Output Parameter:
170 . buff - everything returned from server
171
172 Level: advanced
173
174 .seealso: `PetscHTTPRequest()`, `PetscHTTPSConnect()`, `PetscSSLInitializeContext()`, `PetscSSLDestroyContext()`, `PetscPullJSONValue()`
175 @*/
PetscHTTPSRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],SSL * ssl,char buff[],size_t buffsize)176 PetscErrorCode PetscHTTPSRequest(const char type[], const char url[], const char header[], const char ctype[], const char body[], SSL *ssl, char buff[], size_t buffsize)
177 {
178 char *request;
179 int r;
180 size_t request_len, len;
181 PetscBool foundbody = PETSC_FALSE;
182
183 PetscFunctionBegin;
184 PetscCall(PetscHTTPBuildRequest(type, url, header, ctype, body, &request));
185 PetscCall(PetscStrlen(request, &request_len));
186
187 r = SSL_write(ssl, request, (int)request_len);
188 switch (SSL_get_error(ssl, r)) {
189 case SSL_ERROR_NONE:
190 PetscCheck(request_len == (size_t)r, PETSC_COMM_SELF, PETSC_ERR_LIB, "Incomplete write to SSL socket");
191 break;
192 default:
193 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "SSL socket write problem");
194 }
195
196 /* Now read the server's response, globus sends it in two chunks hence must read a second time if needed */
197 PetscCall(PetscArrayzero(buff, buffsize));
198 len = 0;
199 foundbody = PETSC_FALSE;
200 do {
201 char *clen;
202 int cl;
203 size_t nlen;
204
205 r = SSL_read(ssl, buff + len, (int)buffsize);
206 len += r;
207 switch (SSL_get_error(ssl, r)) {
208 case SSL_ERROR_NONE:
209 break;
210 case SSL_ERROR_ZERO_RETURN:
211 foundbody = PETSC_TRUE;
212 SSL_shutdown(ssl);
213 break;
214 case SSL_ERROR_SYSCALL:
215 foundbody = PETSC_TRUE;
216 break;
217 default:
218 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "SSL read problem");
219 }
220
221 PetscCall(PetscStrstr(buff, "Content-Length: ", &clen));
222 if (clen) {
223 clen += 15;
224 sscanf(clen, "%d", &cl);
225 if (!cl) foundbody = PETSC_TRUE;
226 else {
227 PetscCall(PetscStrstr(buff, "\r\n\r\n", &clen));
228 if (clen) {
229 PetscCall(PetscStrlen(clen, &nlen));
230 if (nlen - 4 == (size_t)cl) foundbody = PETSC_TRUE;
231 }
232 }
233 } else {
234 /* if no content length than must leave because you don't know if you can read again */
235 foundbody = PETSC_TRUE;
236 }
237 } while (!foundbody);
238 PetscCall(PetscInfo(NULL, "HTTPS result follows: \n%s\n", buff));
239
240 SSL_free(ssl);
241 PetscCall(PetscFree(request));
242 PetscFunctionReturn(PETSC_SUCCESS);
243 }
244
245 /*@C
246 PetscHTTPRequest - Send a request to an HTTP server
247
248 Input Parameters:
249 + type - either "POST" or "GET"
250 . url - URL of request host/path
251 . header - additional header information, may be `NULL`
252 . ctype - data type of body, for example application/json
253 . body - data to send to server
254 . sock - obtained with `PetscOpenSocket()`
255 - buffsize - size of buffer
256
257 Output Parameter:
258 . buff - everything returned from server
259
260 Level: advanced
261
262 .seealso: `PetscHTTPSRequest()`, `PetscOpenSocket()`, `PetscHTTPSConnect()`, `PetscPullJSONValue()`
263 @*/
PetscHTTPRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],int sock,char buff[],size_t buffsize)264 PetscErrorCode PetscHTTPRequest(const char type[], const char url[], const char header[], const char ctype[], const char body[], int sock, char buff[], size_t buffsize)
265 {
266 char *request;
267 size_t request_len;
268
269 PetscFunctionBegin;
270 PetscCall(PetscHTTPBuildRequest(type, url, header, ctype, body, &request));
271 PetscCall(PetscStrlen(request, &request_len));
272
273 PetscCall(PetscBinaryWrite(sock, request, request_len, PETSC_CHAR));
274 PetscCall(PetscFree(request));
275 PetscCall(PetscBinaryRead(sock, buff, buffsize, NULL, PETSC_CHAR));
276 buff[buffsize - 1] = 0;
277 PetscCall(PetscInfo(NULL, "HTTP result follows: \n%s\n", buff));
278 PetscFunctionReturn(PETSC_SUCCESS);
279 }
280
281 /*@C
282 PetscHTTPSConnect - connect to a HTTPS server
283
284 Input Parameters:
285 + host - the name of the machine hosting the HTTPS server
286 . port - the port number where the server is hosting, usually 443
287 - ctx - value obtained with `PetscSSLInitializeContext()`
288
289 Output Parameters:
290 + sock - socket to connect
291 - ssl - the argument passed to `PetscHTTPSRequest()`
292
293 Level: advanced
294
295 .seealso: `PetscOpenSocket()`, `PetscHTTPSRequest()`, `PetscSSLInitializeContext()`
296 @*/
PetscHTTPSConnect(const char host[],int port,SSL_CTX * ctx,int * sock,SSL ** ssl)297 PetscErrorCode PetscHTTPSConnect(const char host[], int port, SSL_CTX *ctx, int *sock, SSL **ssl)
298 {
299 BIO *sbio;
300
301 PetscFunctionBegin;
302 /* Connect the TCP socket*/
303 PetscCall(PetscOpenSocket(host, port, sock));
304
305 /* Connect the SSL socket */
306 *ssl = SSL_new(ctx);
307 sbio = BIO_new_socket(*sock, BIO_NOCLOSE);
308 SSL_set_bio(*ssl, sbio, sbio);
309 PetscCheck(SSL_connect(*ssl) > 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "SSL connect error");
310 PetscFunctionReturn(PETSC_SUCCESS);
311 }
312
313 /*@C
314 PetscPullJSONValue - Given a JSON response containing the substring with "key" : "value" where there may or not be spaces around the : returns the value.
315
316 Input Parameters:
317 + buff - the char array containing the possible values
318 . key - the key of the requested value
319 - valuelen - the length of the array to contain the value associated with the key
320
321 Output Parameters:
322 + value - the value obtained
323 - found - flag indicating if the value was found in the buff
324
325 Level: advanced
326
327 .seealso: `PetscOpenSocket()`, `PetscHTTPSRequest()`, `PetscSSLInitializeContext()`, `PetscPushJSONValue()`
328 @*/
PetscPullJSONValue(const char buff[],const char key[],char value[],size_t valuelen,PetscBool * found)329 PetscErrorCode PetscPullJSONValue(const char buff[], const char key[], char value[], size_t valuelen, PetscBool *found)
330 {
331 char *v, *w;
332 char work[256];
333 size_t len;
334
335 PetscFunctionBegin;
336 PetscCall(PetscStrncpy(work, "\"", sizeof(work)));
337 PetscCall(PetscStrlcat(work, key, sizeof(work)));
338 PetscCall(PetscStrlcat(work, "\":", sizeof(work)));
339 PetscCall(PetscStrstr(buff, work, &v));
340 PetscCall(PetscStrlen(work, &len));
341 if (v) {
342 v += len;
343 } else {
344 work[len++ - 1] = 0;
345 PetscCall(PetscStrlcat(work, " :", sizeof(work)));
346 PetscCall(PetscStrstr(buff, work, &v));
347 if (!v) {
348 *found = PETSC_FALSE;
349 PetscFunctionReturn(PETSC_SUCCESS);
350 }
351 v += len;
352 }
353 PetscCall(PetscStrchr(v, '\"', &v));
354 if (!v) {
355 *found = PETSC_FALSE;
356 PetscFunctionReturn(PETSC_SUCCESS);
357 }
358 PetscCall(PetscStrchr(v + 1, '\"', &w));
359 if (!w) {
360 *found = PETSC_FALSE;
361 PetscFunctionReturn(PETSC_SUCCESS);
362 }
363 *found = PETSC_TRUE;
364 PetscCall(PetscStrncpy(value, v + 1, PetscMin((size_t)(w - v), valuelen)));
365 PetscFunctionReturn(PETSC_SUCCESS);
366 }
367
368 #include <ctype.h>
369
370 /*@C
371 PetscPushJSONValue - Puts a "key" : "value" pair onto a string
372
373 Input Parameters:
374 + buff - the char array where the value will be put
375 . key - the key value to be set
376 . value - the value associated with the key
377 - bufflen - the size of the buffer (currently ignored)
378
379 Level: advanced
380
381 Note:
382 Ignores lengths so can cause buffer overflow
383
384 .seealso: `PetscOpenSocket()`, `PetscHTTPSRequest()`, `PetscSSLInitializeContext()`, `PetscPullJSONValue()`
385 @*/
PetscPushJSONValue(char buff[],const char key[],const char value[],size_t bufflen)386 PetscErrorCode PetscPushJSONValue(char buff[], const char key[], const char value[], size_t bufflen)
387 {
388 size_t len;
389 PetscBool special;
390
391 PetscFunctionBegin;
392 PetscCall(PetscStrcmp(value, "null", &special));
393 if (!special) PetscCall(PetscStrcmp(value, "true", &special));
394 if (!special) PetscCall(PetscStrcmp(value, "false", &special));
395 if (!special) {
396 PetscInt i;
397
398 PetscCall(PetscStrlen(value, &len));
399 special = PETSC_TRUE;
400 for (i = 0; i < (int)len; i++) {
401 if (!isdigit(value[i])) {
402 special = PETSC_FALSE;
403 break;
404 }
405 }
406 }
407
408 PetscCall(PetscStrlcat(buff, "\"", bufflen));
409 PetscCall(PetscStrlcat(buff, key, bufflen));
410 PetscCall(PetscStrlcat(buff, "\":", bufflen));
411 if (!special) PetscCall(PetscStrlcat(buff, "\"", bufflen));
412 PetscCall(PetscStrlcat(buff, value, bufflen));
413 if (!special) PetscCall(PetscStrlcat(buff, "\"", bufflen));
414 PetscFunctionReturn(PETSC_SUCCESS);
415 }
416