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