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 PetscCheck(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 PetscCheck(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 PetscCheck(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 PetscCheck(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