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