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