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